diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index 57299ab1c6..88da20434c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -2,7 +2,7 @@ CTL_AddImage=Add Data Source CTL_AddImageButton=Add Data Source CTL_CaseCloseAct=Close Case CTL_CaseNewAction=New Case -CTL_CasePropertiesAction=Case Properties +CTL_CaseDetailsAction=Case Details CTL_CaseDeleteAction=Delete Case Menu/Case/OpenRecentCase=Open Recent Case CTL_CaseDeleteAction=Delete Case @@ -103,7 +103,7 @@ CaseCreateAction.msgDlg.cantCreateCase.msg=Cannot create case IntervalErrorReport.NewIssues=new issue(s) IntervalErrorReport.TotalIssues=total issue(s) IntervalErrorReport.ErrorText=Database Connection Error -CasePropertiesAction.window.title=Case Properties +CaseDetailsAction.window.title=Case Details CueBannerPanel.title.text=Open Recent Case ImageDSProcessor.dsType.text=Disk Image or VM File ImageDSProcessor.allDesc.text=All Supported Types @@ -171,12 +171,6 @@ LocalDiskPanel.copyImageCheckbox.text=Make a VHD image of the drive while it is LocalDiskPanel.jLabel1.text=Note that at least one ingest module must be run to create a complete copy LocalDiskPanel.pathTextField.text= LocalDiskPanel.browseButton.text=Browse -CasePropertiesPanel.caseDirLabel.text=Case Directory: -CasePropertiesPanel.crDateLabel.text=Created Date: -CasePropertiesPanel.caseNameLabel.text=Case Name: -CasePropertiesPanel.lbDbName.text=Database Name: -CasePropertiesPanel.lbDbType.text=Case Type: -CasePropertiesPanel.caseNumberLabel.text=Case Number: LocalDiskPanel.changeDatabasePathCheckbox.text=Update case to use VHD file upon completion CueBannerPanel.openRecentCaseButton.text= CueBannerPanel.openRecentCaseLabel.text=Open Recent Case @@ -233,6 +227,12 @@ ImageFilePanel.md5HashTextField.text= ImageFilePanel.errorLabel.text=Error Label ImageFilePanel.hashValuesNoteLabel.text=NOTE: These values will not be validated when the data source is added. ImageFilePanel.hashValuesLabel.text=Hash Values (optional): +CaseDetailsPanel.crDateLabel.text=Created Date: +CaseDetailsPanel.caseDirLabel.text=Case Directory: +CaseDetailsPanel.caseNumberLabel.text=Case Number: +CaseDetailsPanel.lbDbName.text=Database Name: +CaseDetailsPanel.lbDbType.text=Case Type: +CaseDetailsPanel.caseNameLabel.text=Case Name: OpenMultiUserCasePanel.searchLabel.text=Select any case and start typing to search by case name OpenMultiUserCasePanel.cancelButton.text=Cancel OpenMultiUserCasePanel.openSelectedCaseButton.text=Open Selected Case diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties index b067b28c98..2b05d5cac8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties @@ -166,13 +166,6 @@ Case_caseType_multiUser=\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9 Case_caseType_singleUser=\u5358\u6570\u30e6\u30fc\u30b6\u30fc\u30b1\u30fc\u30b9 CasePropertiesForm.imagesTable.columnModel.title0=\u30d1\u30b9 CasePropertiesForm.imagesTable.columnModel.title1=\u524a\u9664 -CasePropertiesPanel.caseDirLabel.text=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\uff1a -CasePropertiesPanel.crDateLabel.text=\u4f5c\u6210\u65e5\uff1a -CasePropertiesPanel.caseNameLabel.text=\u30b1\u30fc\u30b9\u540d\uff1a -CasePropertiesPanel.lbDbName.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u540d\uff1a -CasePropertiesPanel.lbDbType.text=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7\uff1a -CasePropertiesPanel.caseNumberLabel.text=\u30b1\u30fc\u30b9\u756a\u53f7\uff1a -CasePropertiesPanel.examinerLabel.text=\u8abf\u67fb\u62c5\u5f53\u8005\uff1a OptionalCasePropertiesPanel.examinerLabel.text=\u8abf\u67fb\u62c5\u5f53\u8005\uff1a OptionalCasePropertiesPanel.caseDisplayNameLabel.text=\u30b1\u30fc\u30b9\u756a\u53f7\uff1a CueBannerPanel.openRecentCaseLabel.text=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u958b\u304f @@ -196,4 +189,10 @@ LogicalEvidenceFilePanel.logicalEvidenceFileChooser.dialogTitle=\u30ed\u30fc\u30 LogicalEvidenceFilePanel.logicalEvidenceFileChooser.approveButtonText=\u9078\u629e LocalDiskSelectionDialog.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb LocalDiskSelectionDialog.selectLocalDiskLabel.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af\u3092\u9078\u629e\uff1a +CaseDetailsPanel.crDateLabel.text=\u4f5c\u6210\u65e5\uff1a +CaseDetailsPanel.caseDirLabel.text=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\uff1a +CaseDetailsPanel.caseNumberLabel.text=\u30b1\u30fc\u30b9\u756a\u53f7\uff1a +CaseDetailsPanel.lbDbName.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u540d\uff1a +CaseDetailsPanel.lbDbType.text=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7\uff1a +CaseDetailsPanel.caseNameLabel.text=\u30b1\u30fc\u30b9\u540d\uff1a OpenMultiUserCasePanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 863e610ae1..4e2672fc9b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -68,7 +68,7 @@ import org.sleuthkit.autopsy.appservices.AutopsyService; import org.sleuthkit.autopsy.appservices.AutopsyService.CaseContext; import static org.sleuthkit.autopsy.casemodule.Bundle.*; import org.sleuthkit.autopsy.casemodule.CaseMetadata.CaseMetadataException; -import org.sleuthkit.autopsy.casemodule.datasourceSummary.DataSourceSummaryAction; +import org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryAction; import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceEvent; import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceFailedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; @@ -1172,7 +1172,7 @@ public class Case { */ CallableSystemAction.get(AddImageAction.class).setEnabled(true); CallableSystemAction.get(CaseCloseAction.class).setEnabled(true); - CallableSystemAction.get(CasePropertiesAction.class).setEnabled(true); + CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true); CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true); CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true); CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true); @@ -1226,7 +1226,7 @@ public class Case { */ CallableSystemAction.get(AddImageAction.class).setEnabled(false); CallableSystemAction.get(CaseCloseAction.class).setEnabled(false); - CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false); + CallableSystemAction.get(CaseDetailsAction.class).setEnabled(false); CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(false); CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false); CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseDetailsAction.java similarity index 67% rename from Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesAction.java rename to Core/src/org/sleuthkit/autopsy/casemodule/CaseDetailsAction.java index 2a70c91483..2992a41279 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseDetailsAction.java @@ -31,16 +31,16 @@ import org.openide.util.actions.CallableSystemAction; import org.openide.windows.WindowManager; /** - * The action associated with the Case/Case Properties menu item. It invokes the - * Case Properties dialog. + * The action associated with the Case/Case Details menu item. It invokes the + * dialog containing the CaseInformationPanel. */ -final class CasePropertiesAction extends CallableSystemAction { +final class CaseDetailsAction extends CallableSystemAction { private static final long serialVersionUID = 1L; - private static JDialog casePropertiesDialog; + private static JDialog caseDetailsDialog; - CasePropertiesAction() { - putValue(Action.NAME, NbBundle.getMessage(CasePropertiesAction.class, "CTL_CasePropertiesAction")); + CaseDetailsAction() { + putValue(Action.NAME, NbBundle.getMessage(CaseDetailsAction.class, "CTL_CaseDetailsAction")); this.setEnabled(false); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> { setEnabled(null != evt.getNewValue()); @@ -50,25 +50,25 @@ final class CasePropertiesAction extends CallableSystemAction { @Override public void performAction() { SwingUtilities.invokeLater(() -> { - String title = NbBundle.getMessage(this.getClass(), "CasePropertiesAction.window.title"); + String title = NbBundle.getMessage(this.getClass(), "CaseDetailsAction.window.title"); Frame mainWindow = WindowManager.getDefault().getMainWindow(); - casePropertiesDialog = new JDialog(mainWindow, title, true); + caseDetailsDialog = new JDialog(mainWindow, title, true); CaseInformationPanel caseInformationPanel = new CaseInformationPanel(); caseInformationPanel.addCloseButtonAction((ActionEvent e) -> { - casePropertiesDialog.dispose(); + caseDetailsDialog.dispose(); }); - casePropertiesDialog.add(caseInformationPanel); - casePropertiesDialog.setResizable(true); - casePropertiesDialog.pack(); - casePropertiesDialog.setLocationRelativeTo(mainWindow); - casePropertiesDialog.setVisible(true); - casePropertiesDialog.toFront(); + caseDetailsDialog.add(caseInformationPanel); + caseDetailsDialog.setResizable(true); + caseDetailsDialog.pack(); + caseDetailsDialog.setLocationRelativeTo(mainWindow); + caseDetailsDialog.setVisible(true); + caseDetailsDialog.toFront(); }); } @Override public String getName() { - return NbBundle.getMessage(CasePropertiesAction.class, "CTL_CasePropertiesAction"); + return NbBundle.getMessage(CaseDetailsAction.class, "CTL_CaseDetailsAction"); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/CaseDetailsPanel.form similarity index 92% rename from Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.form rename to Core/src/org/sleuthkit/autopsy/casemodule/CaseDetailsPanel.form index 436ec68cda..07b74b9111 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseDetailsPanel.form @@ -85,8 +85,8 @@ - - + + @@ -186,7 +186,7 @@ - + @@ -207,7 +207,7 @@ - + @@ -223,7 +223,7 @@ - + @@ -258,7 +258,7 @@ - + @@ -279,7 +279,7 @@ - + @@ -291,7 +291,7 @@ - + @@ -319,7 +319,7 @@ - + @@ -356,8 +356,8 @@ - - + + @@ -431,7 +431,7 @@ - + @@ -453,7 +453,7 @@ - + @@ -503,7 +503,7 @@ - + @@ -519,7 +519,7 @@ - + @@ -542,8 +542,8 @@ - - + + @@ -611,7 +611,7 @@ - + @@ -627,14 +627,14 @@ - + - + @@ -650,7 +650,7 @@ - + @@ -676,4 +676,4 @@ - \ No newline at end of file + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseDetailsPanel.java similarity index 91% rename from Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java rename to Core/src/org/sleuthkit/autopsy/casemodule/CaseDetailsPanel.java index 70b5e5dfed..d5b8dbdfa7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseDetailsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,23 +28,23 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization; import org.sleuthkit.autopsy.coreutils.Logger; /** - * A panel that allows the user to view various properties of a case and change + * A panel that allows the user to view various details of a case and change * the display name of the case. */ @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives -final class CasePropertiesPanel extends javax.swing.JPanel { +final class CaseDetailsPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; - private static final Logger logger = Logger.getLogger(CasePropertiesPanel.class.getName()); + private static final Logger logger = Logger.getLogger(CaseDetailsPanel.class.getName()); private Case theCase; /** - * Constructs a panel that allows the user to view various properties of the + * Constructs a panel that allows the user to view various details of the * current case and change the display name of the case. * * @param aCase A case. */ - CasePropertiesPanel(Case caseInfo) { + CaseDetailsPanel(Case caseInfo) { initComponents(); updateCaseInfo(); } @@ -109,18 +109,18 @@ final class CasePropertiesPanel extends javax.swing.JPanel { repaint(); } - @Messages({"CasePropertiesPanel.casePanel.border.title=Case", - "CasePropertiesPanel.lbCaseUUIDLabel.text=Case UUID:", - "CasePropertiesPanel.examinerPanel.border.title=Examiner", - "CasePropertiesPanel.examinerLabel.text=Name:", - "CasePropertiesPanel.lbExaminerPhoneLabel.text=Phone:", - "CasePropertiesPanel.lbExaminerEmailLabel.text=Email:", - "CasePropertiesPanel.lbNotesLabel.text=Notes:", - "CasePropertiesPanel.pnOrganization.border.title=Organization", - "CasePropertiesPanel.lbOrganizationNameLabel.text=Name:", - "CasePropertiesPanel.lbPointOfContactNameLabel.text=Point of Contact:", - "CasePropertiesPanel.lbPointOfContactPhoneLabel.text=Phone:", - "CasePropertiesPanel.lbPointOfContactEmailLabel.text=Email:"}) + @Messages({"CaseDetailsPanel.casePanel.border.title=Case", + "CaseDetailsPanel.lbCaseUUIDLabel.text=Case UUID:", + "CaseDetailsPanel.examinerPanel.border.title=Examiner", + "CaseDetailsPanel.examinerLabel.text=Name:", + "CaseDetailsPanel.lbExaminerPhoneLabel.text=Phone:", + "CaseDetailsPanel.lbExaminerEmailLabel.text=Email:", + "CaseDetailsPanel.lbNotesLabel.text=Notes:", + "CaseDetailsPanel.pnOrganization.border.title=Organization", + "CaseDetailsPanel.lbOrganizationNameLabel.text=Name:", + "CaseDetailsPanel.lbPointOfContactNameLabel.text=Point of Contact:", + "CaseDetailsPanel.lbPointOfContactPhoneLabel.text=Phone:", + "CaseDetailsPanel.lbPointOfContactEmailLabel.text=Email:"}) /** * In this generated code below, there are 2 strings "Path" and "Remove" @@ -182,21 +182,21 @@ final class CasePropertiesPanel extends javax.swing.JPanel { jTextArea1.setRows(5); jScrollPane1.setViewportView(jTextArea1); - casePanel.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.casePanel.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N + casePanel.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.casePanel.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N caseNameLabel.setFont(caseNameLabel.getFont().deriveFont(caseNameLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - caseNameLabel.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.caseNameLabel.text")); // NOI18N + caseNameLabel.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.caseNameLabel.text")); // NOI18N caseNameLabel.setMaximumSize(new java.awt.Dimension(82, 14)); caseNameLabel.setMinimumSize(new java.awt.Dimension(82, 14)); caseNameLabel.setPreferredSize(new java.awt.Dimension(82, 14)); lbDbType.setFont(lbDbType.getFont().deriveFont(lbDbType.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - lbDbType.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.lbDbType.text")); // NOI18N + lbDbType.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.lbDbType.text")); // NOI18N lbDbType.setMaximumSize(new java.awt.Dimension(82, 14)); lbDbType.setMinimumSize(new java.awt.Dimension(82, 14)); lbDbType.setPreferredSize(new java.awt.Dimension(82, 14)); - lbCaseUUIDLabel.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.lbCaseUUIDLabel.text")); // NOI18N + lbCaseUUIDLabel.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.lbCaseUUIDLabel.text")); // NOI18N lbCaseUUIDLabel.setMaximumSize(new java.awt.Dimension(82, 14)); lbCaseUUIDLabel.setMinimumSize(new java.awt.Dimension(82, 14)); lbCaseUUIDLabel.setPreferredSize(new java.awt.Dimension(82, 14)); @@ -206,16 +206,16 @@ final class CasePropertiesPanel extends javax.swing.JPanel { dbNameField.setMinimumSize(new java.awt.Dimension(25, 14)); lbDbName.setFont(lbDbName.getFont().deriveFont(lbDbName.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - lbDbName.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.lbDbName.text")); // NOI18N + lbDbName.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.lbDbName.text")); // NOI18N lbDbName.setMaximumSize(new java.awt.Dimension(82, 14)); lbDbName.setMinimumSize(new java.awt.Dimension(82, 14)); lbDbName.setPreferredSize(new java.awt.Dimension(82, 14)); caseNumberLabel.setFont(caseNumberLabel.getFont().deriveFont(caseNumberLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - caseNumberLabel.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.caseNumberLabel.text")); // NOI18N + caseNumberLabel.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.caseNumberLabel.text")); // NOI18N caseDirLabel.setFont(caseDirLabel.getFont().deriveFont(caseDirLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - caseDirLabel.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.caseDirLabel.text")); // NOI18N + caseDirLabel.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.caseDirLabel.text")); // NOI18N caseDirLabel.setMaximumSize(new java.awt.Dimension(82, 14)); caseDirLabel.setMinimumSize(new java.awt.Dimension(82, 14)); caseDirLabel.setPreferredSize(new java.awt.Dimension(82, 14)); @@ -223,7 +223,7 @@ final class CasePropertiesPanel extends javax.swing.JPanel { caseDirField.setMinimumSize(new java.awt.Dimension(25, 14)); crDateLabel.setFont(crDateLabel.getFont().deriveFont(crDateLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - crDateLabel.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.crDateLabel.text")); // NOI18N + crDateLabel.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.crDateLabel.text")); // NOI18N crDateLabel.setMaximumSize(new java.awt.Dimension(82, 14)); crDateLabel.setMinimumSize(new java.awt.Dimension(82, 14)); crDateLabel.setPreferredSize(new java.awt.Dimension(82, 14)); @@ -301,16 +301,16 @@ final class CasePropertiesPanel extends javax.swing.JPanel { .addGap(6, 6, 6)) ); - examinerPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.examinerPanel.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N + examinerPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.examinerPanel.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N - lbNotesLabel.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.lbNotesLabel.text")); // NOI18N + lbNotesLabel.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.lbNotesLabel.text")); // NOI18N lbNotesLabel.setMaximumSize(new java.awt.Dimension(82, 14)); lbNotesLabel.setMinimumSize(new java.awt.Dimension(82, 14)); lbNotesLabel.setPreferredSize(new java.awt.Dimension(82, 14)); lbNotesLabel.setRequestFocusEnabled(false); examinerLabel.setFont(examinerLabel.getFont().deriveFont(examinerLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); - examinerLabel.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.examinerLabel.text")); // NOI18N + examinerLabel.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.examinerLabel.text")); // NOI18N examinerLabel.setMaximumSize(new java.awt.Dimension(82, 14)); examinerLabel.setMinimumSize(new java.awt.Dimension(82, 14)); examinerLabel.setPreferredSize(new java.awt.Dimension(82, 14)); @@ -329,12 +329,12 @@ final class CasePropertiesPanel extends javax.swing.JPanel { taNotesText.setOpaque(false); caseNotesScrollPane.setViewportView(taNotesText); - lbExaminerEmailLabel.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.lbExaminerEmailLabel.text")); // NOI18N + lbExaminerEmailLabel.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.lbExaminerEmailLabel.text")); // NOI18N lbExaminerEmailLabel.setMaximumSize(new java.awt.Dimension(82, 14)); lbExaminerEmailLabel.setMinimumSize(new java.awt.Dimension(82, 14)); lbExaminerEmailLabel.setPreferredSize(new java.awt.Dimension(82, 14)); - lbExaminerPhoneLabel.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.lbExaminerPhoneLabel.text")); // NOI18N + lbExaminerPhoneLabel.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.lbExaminerPhoneLabel.text")); // NOI18N lbExaminerPhoneLabel.setMaximumSize(new java.awt.Dimension(82, 14)); lbExaminerPhoneLabel.setMinimumSize(new java.awt.Dimension(82, 14)); lbExaminerPhoneLabel.setPreferredSize(new java.awt.Dimension(82, 14)); @@ -389,21 +389,21 @@ final class CasePropertiesPanel extends javax.swing.JPanel { .addGap(6, 6, 6)) ); - pnOrganization.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.pnOrganization.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N + pnOrganization.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.pnOrganization.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N - lbOrganizationNameLabel.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.lbOrganizationNameLabel.text")); // NOI18N + lbOrganizationNameLabel.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.lbOrganizationNameLabel.text")); // NOI18N lbOrganizationNameLabel.setMaximumSize(new java.awt.Dimension(82, 14)); lbOrganizationNameLabel.setMinimumSize(new java.awt.Dimension(82, 14)); lbOrganizationNameLabel.setPreferredSize(new java.awt.Dimension(82, 14)); - lbPointOfContactNameLabel.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.lbPointOfContactNameLabel.text")); // NOI18N + lbPointOfContactNameLabel.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.lbPointOfContactNameLabel.text")); // NOI18N - lbPointOfContactEmailLabel.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.lbPointOfContactEmailLabel.text")); // NOI18N + lbPointOfContactEmailLabel.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.lbPointOfContactEmailLabel.text")); // NOI18N lbPointOfContactEmailLabel.setMaximumSize(new java.awt.Dimension(82, 14)); lbPointOfContactEmailLabel.setMinimumSize(new java.awt.Dimension(82, 14)); lbPointOfContactEmailLabel.setPreferredSize(new java.awt.Dimension(82, 14)); - lbPointOfContactPhoneLabel.setText(org.openide.util.NbBundle.getMessage(CasePropertiesPanel.class, "CasePropertiesPanel.lbPointOfContactPhoneLabel.text")); // NOI18N + lbPointOfContactPhoneLabel.setText(org.openide.util.NbBundle.getMessage(CaseDetailsPanel.class, "CaseDetailsPanel.lbPointOfContactPhoneLabel.text")); // NOI18N lbPointOfContactPhoneLabel.setMaximumSize(new java.awt.Dimension(82, 14)); lbPointOfContactPhoneLabel.setMinimumSize(new java.awt.Dimension(82, 14)); lbPointOfContactPhoneLabel.setPreferredSize(new java.awt.Dimension(82, 14)); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.form index 3af89b45de..33471dde04 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.form @@ -35,40 +35,38 @@ - - + + + + - - + + + + + + + - - - - - - - - - @@ -86,6 +84,21 @@ + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java index 252a44e577..a6494fe22b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,25 +22,22 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.logging.Level; import javax.swing.JDialog; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import org.sleuthkit.autopsy.coreutils.Logger; import org.openide.util.NbBundle.Messages; import org.openide.windows.WindowManager; /** - * Panel for displaying the case information, including both case details and - * ingest job history. + * Panel for displaying the case information, including case details. */ @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class CaseInformationPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; - CasePropertiesPanel propertiesPanel; + private CaseDetailsPanel caseDetailsPanel; /** - * Constructs a panel for displaying the case information, including both - * case details and ingest job history. + * Constructs a panel for displaying the case information including case + * details. */ CaseInformationPanel() { initComponents(); @@ -49,25 +46,18 @@ class CaseInformationPanel extends javax.swing.JPanel { @Messages({ "CaseInformationPanel.caseDetails.header=Details", - "CaseInformationPanel.ingestJobInfo.header=Ingest History", "CaseInformationPanel.editDetailsButton.text=Edit Details", "CaseInformationPanel.editDetailsDialog.title=Edit Case Details" }) private void customizeComponents() { try { - propertiesPanel = new CasePropertiesPanel(Case.getCurrentCaseThrows()); - } catch (NoCurrentCaseException ex) { + caseDetailsPanel = new CaseDetailsPanel(Case.getCurrentCaseThrows()); + } catch (NoCurrentCaseException ex) { Logger.getLogger(CaseInformationPanel.class.getName()).log(Level.INFO, "Exception while getting open case.", ex); } - propertiesPanel.setSize(propertiesPanel.getPreferredSize()); - this.tabbedPane.addTab(Bundle.CaseInformationPanel_caseDetails_header(), propertiesPanel); - this.tabbedPane.addTab(Bundle.CaseInformationPanel_ingestJobInfo_header(), new IngestJobInfoPanel()); - this.tabbedPane.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - tabbedPane.getSelectedComponent().setSize(tabbedPane.getSelectedComponent().getPreferredSize()); - } - }); + caseDetailsPanel.setSize(caseDetailsPanel.getPreferredSize()); + this.detailsPanel.add(caseDetailsPanel); + this.detailsPanel.setPreferredSize(caseDetailsPanel.getPreferredSize()); } /** @@ -89,11 +79,9 @@ class CaseInformationPanel extends javax.swing.JPanel { private void initComponents() { outerDetailsPanel = new javax.swing.JPanel(); - tabbedPane = new javax.swing.JTabbedPane(); closeButton = new javax.swing.JButton(); editDetailsButton = new javax.swing.JButton(); - - tabbedPane.setPreferredSize(new java.awt.Dimension(420, 200)); + detailsPanel = new javax.swing.JPanel(); org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(CaseInformationPanel.class, "CaseInformationPanel.closeButton.text")); // NOI18N @@ -104,27 +92,42 @@ class CaseInformationPanel extends javax.swing.JPanel { } }); + javax.swing.GroupLayout detailsPanelLayout = new javax.swing.GroupLayout(detailsPanel); + detailsPanel.setLayout(detailsPanelLayout); + detailsPanelLayout.setHorizontalGroup( + detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 709, Short.MAX_VALUE) + ); + detailsPanelLayout.setVerticalGroup( + detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 219, Short.MAX_VALUE) + ); + javax.swing.GroupLayout outerDetailsPanelLayout = new javax.swing.GroupLayout(outerDetailsPanel); outerDetailsPanel.setLayout(outerDetailsPanelLayout); outerDetailsPanelLayout.setHorizontalGroup( outerDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(tabbedPane, javax.swing.GroupLayout.DEFAULT_SIZE, 709, Short.MAX_VALUE) .addGroup(outerDetailsPanelLayout.createSequentialGroup() .addContainerGap() .addComponent(editDetailsButton, javax.swing.GroupLayout.PREFERRED_SIZE, 128, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 502, Short.MAX_VALUE) .addComponent(closeButton) .addContainerGap()) + .addGroup(outerDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(detailsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); outerDetailsPanelLayout.setVerticalGroup( outerDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(outerDetailsPanelLayout.createSequentialGroup() - .addComponent(tabbedPane, javax.swing.GroupLayout.DEFAULT_SIZE, 228, Short.MAX_VALUE) - .addGap(0, 0, 0) + .addContainerGap(228, Short.MAX_VALUE) .addGroup(outerDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(closeButton) .addComponent(editDetailsButton)) .addContainerGap()) + .addGroup(outerDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(outerDetailsPanelLayout.createSequentialGroup() + .addComponent(detailsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 43, Short.MAX_VALUE))) ); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); @@ -151,7 +154,7 @@ class CaseInformationPanel extends javax.swing.JPanel { editCasePropertiesPanel.addSaveButtonAction((ActionEvent e) -> { editCasePropertiesDialog.setVisible(false); editCasePropertiesPanel.saveProperties(); - propertiesPanel.updateCaseInfo(); + caseDetailsPanel.updateCaseInfo(); }); @@ -161,13 +164,13 @@ class CaseInformationPanel extends javax.swing.JPanel { editCasePropertiesDialog.setLocationRelativeTo(this); editCasePropertiesDialog.setVisible(true); editCasePropertiesDialog.toFront(); - propertiesPanel.updateCaseInfo(); + caseDetailsPanel.updateCaseInfo(); }//GEN-LAST:event_editDetailsButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton closeButton; + private javax.swing.JPanel detailsPanel; private javax.swing.JButton editDetailsButton; private javax.swing.JPanel outerDetailsPanel; - private javax.swing.JTabbedPane tabbedPane; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java index e274245edd..ad482705b3 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * - * Copyright 2011-2018 Basis Technology Corp. + * + * Copyright 2011-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. @@ -35,6 +35,7 @@ import org.sleuthkit.datamodel.IngestJobInfo; import org.sleuthkit.datamodel.IngestModuleInfo; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.DataSource; /** * Panel for displaying ingest job history. @@ -44,9 +45,11 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(IngestJobInfoPanel.class.getName()); private List ingestJobs; + private final List ingestJobsForSelectedDataSource = new ArrayList<>(); private IngestJobTableModel ingestJobTableModel = new IngestJobTableModel(); private IngestModuleTableModel ingestModuleTableModel = new IngestModuleTableModel(null); private final DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + private DataSource selectedDataSource; /** * Creates new form IngestJobInfoPanel @@ -61,7 +64,7 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { private void customizeComponents() { refresh(); this.ingestJobTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> { - IngestJobInfo currJob = (ingestJobTable.getSelectedRow() < 0 ? null : this.ingestJobs.get(ingestJobTable.getSelectedRow())); + IngestJobInfo currJob = (ingestJobTable.getSelectedRow() < 0 ? null : this.ingestJobsForSelectedDataSource.get(ingestJobTable.getSelectedRow())); this.ingestModuleTableModel = new IngestModuleTableModel(currJob); this.ingestModuleTable.setModel(this.ingestModuleTableModel); }); @@ -75,20 +78,45 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { }); } + /** + * Update the data source which ingest jobs are being displayed for + * + * @param selectedDataSource the data source to display ingest jobs for + */ + public void updateIngestHistoryData(DataSource selectedDataSource) { + this.selectedDataSource = selectedDataSource; + ingestJobsForSelectedDataSource.clear(); + if (selectedDataSource != null) { + for (IngestJobInfo jobInfo : ingestJobs) { + if (selectedDataSource.getId() == jobInfo.getObjectId()) { + ingestJobsForSelectedDataSource.add(jobInfo); + } + } + } + this.ingestJobTableModel = new IngestJobTableModel(); + this.ingestJobTable.setModel(ingestJobTableModel); + //if there were ingest jobs select the first one by default + if (!ingestJobsForSelectedDataSource.isEmpty()) { + ingestJobTable.setRowSelectionInterval(0, 0); + } + this.repaint(); + } + + /** + * Get the updated complete list of ingest jobs. + */ private void refresh() { try { SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - List ingestJobs = skCase.getIngestJobs(); - this.ingestJobs = ingestJobs; - this.repaint(); + this.ingestJobs = skCase.getIngestJobs(); + updateIngestHistoryData(selectedDataSource); } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Failed to load ingest jobs.", ex); JOptionPane.showMessageDialog(this, Bundle.IngestJobInfoPanel_loadIngestJob_error_text(), Bundle.IngestJobInfoPanel_loadIngestJob_error_title(), JOptionPane.ERROR_MESSAGE); } } - @Messages({"IngestJobInfoPanel.IngestJobTableModel.DataSource.header=Data Source", - "IngestJobInfoPanel.IngestJobTableModel.StartTime.header=Start Time", + @Messages({"IngestJobInfoPanel.IngestJobTableModel.StartTime.header=Start Time", "IngestJobInfoPanel.IngestJobTableModel.EndTime.header=End Time", "IngestJobInfoPanel.IngestJobTableModel.IngestStatus.header=Ingest Status"}) private class IngestJobTableModel extends AbstractTableModel { @@ -96,7 +124,6 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { private final List columnHeaders = new ArrayList<>(); IngestJobTableModel() { - columnHeaders.add(Bundle.IngestJobInfoPanel_IngestJobTableModel_DataSource_header()); columnHeaders.add(Bundle.IngestJobInfoPanel_IngestJobTableModel_StartTime_header()); columnHeaders.add(Bundle.IngestJobInfoPanel_IngestJobTableModel_EndTime_header()); columnHeaders.add(Bundle.IngestJobInfoPanel_IngestJobTableModel_IngestStatus_header()); @@ -104,7 +131,7 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { @Override public int getRowCount() { - return ingestJobs.size(); + return ingestJobsForSelectedDataSource.size(); } @Override @@ -114,24 +141,16 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { @Override public Object getValueAt(int rowIndex, int columnIndex) { - IngestJobInfo currIngestJob = ingestJobs.get(rowIndex); + IngestJobInfo currIngestJob = ingestJobsForSelectedDataSource.get(rowIndex); if (columnIndex == 0) { - try { - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - return skCase.getContentById(currIngestJob.getObjectId()).getName(); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Failed to get content from db", ex); - return ""; - } - } else if (columnIndex == 1) { return datetimeFormat.format(currIngestJob.getStartDateTime()); - } else if (columnIndex == 2) { + } else if (columnIndex == 1) { Date endDate = currIngestJob.getEndDateTime(); if (endDate.getTime() == 0) { return "N/A"; } return datetimeFormat.format(currIngestJob.getEndDateTime()); - } else if (columnIndex == 3) { + } else if (columnIndex == 2) { return currIngestJob.getStatus().getDisplayName(); } return null; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties deleted file mode 100644 index 5cb3d8597d..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties +++ /dev/null @@ -1,5 +0,0 @@ -DataSourceSummaryPanel.fileCountsLabel.text=Files (based on MIME type) -DataSourceSummaryPanel.ingestJobsLabel.text=Ingest Jobs -DataSourceSummaryPanel.closeButton.text=Close -DataSourceSummaryPanel.gotoDataSourceButton.text=Goto Data Source -DataSourceSummaryPanel.operatingSystemLabel.text=OS: diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryPanel.form deleted file mode 100644 index 04e4572ca4..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryPanel.form +++ /dev/null @@ -1,182 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryPanel.java deleted file mode 100644 index 25f97d8937..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryPanel.java +++ /dev/null @@ -1,735 +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.casemodule.datasourceSummary; - -import java.awt.Rectangle; -import java.awt.event.ActionListener; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JOptionPane; -import javax.swing.event.ListSelectionEvent; -import javax.swing.table.AbstractTableModel; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.datamodel.utils.FileTypeUtils; -import org.sleuthkit.autopsy.directorytree.ViewContextAction; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.CaseDbAccessManager.CaseDbAccessQueryCallback; -import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.IngestJobInfo; -import org.sleuthkit.datamodel.OSInfo; -import org.sleuthkit.datamodel.OSUtility; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; -import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; - -final class DataSourceSummaryPanel extends javax.swing.JPanel { - - private static final long serialVersionUID = 1L; - private final List allIngestJobs = new ArrayList<>(); - private List ingestJobs = new ArrayList<>(); - private DataSourceTableModel dataSourceTableModel = new DataSourceTableModel(); - private IngestJobTableModel ingestJobTableModel = new IngestJobTableModel(); - private FilesTableModel filesTableModel = new FilesTableModel(null); - private final List dataSources = new ArrayList<>(); - private final DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); - private static final Logger logger = Logger.getLogger(DataSourceSummaryPanel.class.getName()); - private List osInfoList; - - /** - * Creates new form DataSourceSummaryPanel for displaying a summary of the - * data sources for the fcurrent case and the contents found for each - * datasource. - */ - @Messages({"DataSourceSummaryPanel.getDataSources.error.text=Failed to get the list of datasources for the current case.", - "DataSourceSummaryPanel.getDataSources.error.title=Load Failure"}) - DataSourceSummaryPanel() { - initComponents(); - ingestJobsTable.getTableHeader().setReorderingAllowed(false); - fileCountsTable.getTableHeader().setReorderingAllowed(false); - dataSourcesTable.getTableHeader().setReorderingAllowed(false); - try { - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - allIngestJobs.addAll(skCase.getIngestJobs()); - dataSources.addAll(skCase.getDataSources()); - //if for some reason multiple OS_INFO_ARTIFACTS were created with the same parent object id this will only return one OSInfo object for them - osInfoList = OSUtility.getOSInfo(skCase); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Failed to load ingest jobs.", ex); - JOptionPane.showMessageDialog(this, Bundle.DataSourceSummaryPanel_getDataSources_error_text(), Bundle.DataSourceSummaryPanel_getDataSources_error_title(), JOptionPane.ERROR_MESSAGE); - } - dataSourcesTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> { - if (!e.getValueIsAdjusting()) { - DataSource selectedDataSource = (dataSourcesTable.getSelectedRow() < 0 ? null : dataSources.get(dataSourcesTable.getSelectedRow())); - gotoDataSourceButton.setEnabled(selectedDataSource != null); - updateIngestJobs(selectedDataSource); - filesTableModel = new FilesTableModel(selectedDataSource); - fileCountsTable.setModel(filesTableModel); - operatingSystemValueLabel.setText(getOSName(selectedDataSource)); - this.repaint(); - } - }); - } - - /** - * Get the name of the operating system if it is available. Otherwise get - * and empty string. - * - * @param selectedDataSource the datasource to get the OS information for - * - * @return the name of the operating system on the specified datasource, - * empty string if no operating system info found - */ - private String getOSName(DataSource selectedDataSource) { - String osName = ""; - if (selectedDataSource != null) { - for (OSInfo osInfo : osInfoList) { - try { - //assumes only one Operating System per datasource - //get the datasource id from the OSInfo's first artifact if it has artifacts - if (!osInfo.getArtifacts().isEmpty() && osInfo.getArtifacts().get(0).getDataSource().getId() == selectedDataSource.getId()) { - if (!osName.isEmpty()) { - osName += ", "; - } - osName += osInfo.getOSName(); - } - } catch (TskCoreException ignored) { - //unable to get datasource for the OSInfo Object - //continue checking for OSInfo objects to try and get get the desired information - } - } - } - return osName; - } - - /** - * Update the ingestJobs list with the ingest jobs for the - * selectedDataSource - * - * @param selectedDataSource the datasource to find the ingest jobs for - */ - @Messages({"DataSourceSummaryPanel.loadIngestJob.error.text=Failed to load ingest jobs.", - "DataSourceSummaryPanel.loadIngestJob.error.title=Load Failure"}) - private void updateIngestJobs(DataSource selectedDataSource) { - ingestJobs.clear(); - if (selectedDataSource != null) { - for (IngestJobInfo ingestJob : allIngestJobs) { - if (ingestJob.getObjectId() == selectedDataSource.getId()) { - ingestJobs.add(ingestJob); - } - } - } - ingestJobTableModel = new IngestJobTableModel(); - ingestJobsTable.setModel(ingestJobTableModel); - } - - /** - * 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() { - - jSeparator1 = new javax.swing.JSeparator(); - dataSourcesScrollPane = new javax.swing.JScrollPane(); - dataSourcesTable = new javax.swing.JTable(); - ingestJobsScrollPane = new javax.swing.JScrollPane(); - ingestJobsTable = new javax.swing.JTable(); - fileCountsScrollPane = new javax.swing.JScrollPane(); - fileCountsTable = new javax.swing.JTable(); - operatingSystemLabel = new javax.swing.JLabel(); - operatingSystemValueLabel = new javax.swing.JLabel(); - fileCountsLabel = new javax.swing.JLabel(); - ingestJobsLabel = new javax.swing.JLabel(); - closeButton = new javax.swing.JButton(); - gotoDataSourceButton = new javax.swing.JButton(); - - dataSourcesTable.setModel(dataSourceTableModel); - dataSourcesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); - dataSourcesScrollPane.setViewportView(dataSourcesTable); - - ingestJobsTable.setModel(ingestJobTableModel); - ingestJobsTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); - ingestJobsScrollPane.setViewportView(ingestJobsTable); - - fileCountsTable.setModel(filesTableModel); - fileCountsScrollPane.setViewportView(fileCountsTable); - - org.openide.awt.Mnemonics.setLocalizedText(operatingSystemLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryPanel.class, "DataSourceSummaryPanel.operatingSystemLabel.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(fileCountsLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryPanel.class, "DataSourceSummaryPanel.fileCountsLabel.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(ingestJobsLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryPanel.class, "DataSourceSummaryPanel.ingestJobsLabel.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(DataSourceSummaryPanel.class, "DataSourceSummaryPanel.closeButton.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(gotoDataSourceButton, org.openide.util.NbBundle.getMessage(DataSourceSummaryPanel.class, "DataSourceSummaryPanel.gotoDataSourceButton.text")); // NOI18N - gotoDataSourceButton.setEnabled(false); - gotoDataSourceButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - gotoDataSourceButtonActionPerformed(evt); - } - }); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jSeparator1, javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(dataSourcesScrollPane, javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGap(0, 0, Short.MAX_VALUE) - .addComponent(gotoDataSourceButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(closeButton)) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(fileCountsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(fileCountsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(ingestJobsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(ingestJobsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 474, Short.MAX_VALUE) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(operatingSystemLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(operatingSystemValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))) - .addContainerGap()) - ); - - layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {closeButton, gotoDataSourceButton}); - - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(8, 8, 8) - .addComponent(dataSourcesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 120, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 5, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(fileCountsLabel) - .addComponent(ingestJobsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(fileCountsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(ingestJobsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(operatingSystemLabel, javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(operatingSystemValueLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(10, 10, 10) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(closeButton) - .addComponent(gotoDataSourceButton)) - .addContainerGap()) - ); - }// //GEN-END:initComponents - - /** - * Adds an action listener to the Close button of the panel. - * - * @param action - */ - void addCloseButtonAction(ActionListener action) { - this.closeButton.addActionListener(action); - //the gotoDataSourceButton should also close the dialog - this.gotoDataSourceButton.addActionListener(action); - } - - /** - * Select the data source with the specicied data source id. If no data - * source matches the dataSourceID it will select the first datasource. - * - * @param dataSourceID the ID of the datasource to select, null will cause - * the first datasource to be selected - */ - void selectDataSource(Long dataSourceID) { - if (dataSourceID != null) { - for (int i = 0; i < dataSources.size(); i++) { - if (dataSources.get(i).getId() == dataSourceID) { - dataSourcesTable.setRowSelectionInterval(i, i); - //scroll down from top of table to where selected datasource is - dataSourcesTable.scrollRectToVisible(new Rectangle(dataSourcesTable.getCellRect(i, 0, true))); - return; - } - } - } - //if there are data sources in the list and none were found that matched the specied dataSourceID select the first one - if (!dataSources.isEmpty()) { - dataSourcesTable.setRowSelectionInterval(0, 0); - } - } - - /** - * Performed when the Goto Data Source button is clicked, will cause the - * window to be closed and the data source which was selected to be - * navigated to in the tree. - */ - private void gotoDataSourceButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_gotoDataSourceButtonActionPerformed - //the dialog will be closed due to the action listener added in addCloseButtonAction - DataSource selectedDataSource = (dataSourcesTable.getSelectedRow() < 0 ? null : dataSources.get(dataSourcesTable.getSelectedRow())); - if (selectedDataSource != null) { - new ViewContextAction("", selectedDataSource).actionPerformed(evt); - } - }//GEN-LAST:event_gotoDataSourceButtonActionPerformed - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton closeButton; - private javax.swing.JScrollPane dataSourcesScrollPane; - private javax.swing.JTable dataSourcesTable; - private javax.swing.JLabel fileCountsLabel; - private javax.swing.JScrollPane fileCountsScrollPane; - private javax.swing.JTable fileCountsTable; - private javax.swing.JButton gotoDataSourceButton; - private javax.swing.JLabel ingestJobsLabel; - private javax.swing.JScrollPane ingestJobsScrollPane; - private javax.swing.JTable ingestJobsTable; - private javax.swing.JSeparator jSeparator1; - private javax.swing.JLabel operatingSystemLabel; - private javax.swing.JLabel operatingSystemValueLabel; - // End of variables declaration//GEN-END:variables - - /** - * Table model for the Data source table, to display all data sources for - * the current case. - */ - @Messages({"DataSourceSummaryPanel.DataSourceTableModel.dataSourceName.header=Data Source Name", - "DataSourceSummaryPanel.DataSourceTableModel.type.header=Type", - "DataSourceSummaryPanel.DataSourceTableModel.files.header=Files", - "DataSourceSummaryPanel.DataSourceTableModel.results.header=Results", - "DataSourceSummaryPanel.DataSourceTableModel.tags.header=Tags"}) - private class DataSourceTableModel extends AbstractTableModel { - - private static final long serialVersionUID = 1L; - - private final List columnHeaders = new ArrayList<>(); - private final Map fileCountsMap; - private final Map artifactCountsMap; - private final Map tagCountsMap; - private final Map typesMap; - - /** - * Create a new DataSourceTableModel for the current case. - */ - DataSourceTableModel() { - columnHeaders.add(Bundle.DataSourceSummaryPanel_DataSourceTableModel_dataSourceName_header()); - columnHeaders.add(Bundle.DataSourceSummaryPanel_DataSourceTableModel_type_header()); - columnHeaders.add(Bundle.DataSourceSummaryPanel_DataSourceTableModel_files_header()); - columnHeaders.add(Bundle.DataSourceSummaryPanel_DataSourceTableModel_results_header()); - columnHeaders.add(Bundle.DataSourceSummaryPanel_DataSourceTableModel_tags_header()); - fileCountsMap = getCountsOfFiles(); - artifactCountsMap = getCountsOfArtifacts(); - tagCountsMap = getCountsOfTags(); - typesMap = getDataSourceTypes(); - } - - @Override - public int getRowCount() { - return dataSources.size(); - } - - @Override - public int getColumnCount() { - return columnHeaders.size(); - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - DataSource currentDataSource = dataSources.get(rowIndex); - Long count; - switch (columnIndex) { - case 0: - return currentDataSource.getName(); - case 1: - return typesMap.get(currentDataSource.getId()); - case 2: - //display 0 if no count is found - count = fileCountsMap.get(currentDataSource.getId()); - return count == null ? 0 : count; - case 3: - //display 0 if no count is found - count = artifactCountsMap.get(currentDataSource.getId()); - return count == null ? 0 : count; - case 4: - //display 0 if no count is found - count = tagCountsMap.get(currentDataSource.getId()); - return count == null ? 0 : count; - default: - break; - } - return null; - } - - /** - * Get a map containing the TSK_DATA_SOURCE_USAGE description attributes - * associated with each data source in the current case. - * - * @return Collection which maps datasource id to a String which - * displays a comma seperated list of values of data source - * usage types expected to be in the datasource - */ - private Map getDataSourceTypes() { - try { - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - List listOfArtifacts = skCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE); - Map typeMap = new HashMap<>(); - for (BlackboardArtifact typeArtifact : listOfArtifacts) { - BlackboardAttribute descriptionAttr = typeArtifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DESCRIPTION)); - if (typeArtifact.getDataSource() != null && descriptionAttr != null) { - long dsId = typeArtifact.getDataSource().getId(); - String type = typeMap.get(typeArtifact.getDataSource().getId()); - if (type == null) { - type = descriptionAttr.getValueString(); - } else { - type = type + ", " + descriptionAttr.getValueString(); - } - typeMap.put(dsId, type); - } - } - return typeMap; - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get counts of files for all datasources, providing empty results", ex); - return Collections.emptyMap(); - } - } - - /** - * Get a map containing the number of files in each data source in the - * current case. - * - * @return Collection which maps datasource id to a count for the number - * of files in the datasource, will only contain entries for - * datasources which have at least 1 file - */ - private Map getCountsOfFiles() { - try { - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - DataSourceCountsCallback callback = new DataSourceCountsCallback(); - final String countFilesQuery = "data_source_obj_id, COUNT(*) AS count" - + " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() - + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() - + " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS - skCase.getCaseDbAccessManager().select(countFilesQuery, callback); - return callback.getMapOfCounts(); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get counts of files for all datasources, providing empty results", ex); - return Collections.emptyMap(); - } - } - - /** - * Get a map containing the number of artifacts in each data source in - * the current case. - * - * @return Collection which maps datasource id to a count for the number - * of artifacts in the datasource, will only contain entries for - * datasources which have at least 1 artifact - */ - private Map getCountsOfArtifacts() { - try { - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - DataSourceCountsCallback callback = new DataSourceCountsCallback(); - final String countArtifactsQuery = "data_source_obj_id, COUNT(*) AS count" - + " FROM blackboard_artifacts WHERE review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID() - + " GROUP BY data_source_obj_id"; //NON-NLS - skCase.getCaseDbAccessManager().select(countArtifactsQuery, callback); - return callback.getMapOfCounts(); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get counts of artifacts for all datasources, providing empty results", ex); - return Collections.emptyMap(); - } - } - - /** - * Get a map containing the number of tags which have been applied in - * each data source in the current case. Not necessarily the same as the - * number of items tagged, as an item can have any number of tags. - * - * @return Collection which maps datasource id to a count for the number - * of tags which have been applied in the datasource, will only - * contain entries for datasources which have at least 1 item - * tagged. - */ - private Map getCountsOfTags() { - try { - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - DataSourceCountsCallback fileCountcallback = new DataSourceCountsCallback(); - final String countFileTagsQuery = "data_source_obj_id, COUNT(*) AS count" - + " FROM content_tags as content_tags, tsk_files as tsk_files" - + " WHERE content_tags.obj_id = tsk_files.obj_id" - + " GROUP BY data_source_obj_id"; //NON-NLS - skCase.getCaseDbAccessManager().select(countFileTagsQuery, fileCountcallback); - Map tagCountMap = new HashMap<>(fileCountcallback.getMapOfCounts()); - DataSourceCountsCallback artifactCountcallback = new DataSourceCountsCallback(); - final String countArtifactTagsQuery = "data_source_obj_id, COUNT(*) AS count" - + " FROM blackboard_artifact_tags as artifact_tags, blackboard_artifacts AS arts" - + " WHERE artifact_tags.artifact_id = arts.artifact_id" - + " GROUP BY data_source_obj_id"; //NON-NLS - skCase.getCaseDbAccessManager().select(countArtifactTagsQuery, artifactCountcallback); - //combine the results from the count artifact tags query into the copy of the mapped results from the count file tags query - artifactCountcallback.getMapOfCounts().forEach((key, value) -> tagCountMap.merge(key, value, (value1, value2) -> value1 + value2)); - return tagCountMap; - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get counts of tags for all datasources, providing empty results", ex); - return Collections.emptyMap(); - } - - } - - @Override - public String getColumnName(int column) { - return columnHeaders.get(column); - } - - /** - * Get the map of Data Source ID to counts of items found for a query - * which selects data_source_obj_id and count(*) with a group by - * data_source_obj_id clause. - */ - private class DataSourceCountsCallback implements CaseDbAccessQueryCallback { - - Map dataSourceObjIdCounts = new HashMap<>(); - - @Override - public void process(ResultSet rs) { - try { - while (rs.next()) { - try { - dataSourceObjIdCounts.put(rs.getLong("data_source_obj_id"), rs.getLong("count")); - } catch (SQLException ex) { - logger.log(Level.WARNING, "Unable to get data_source_obj_id or count from result set", ex); - } - } - } catch (SQLException ex) { - logger.log(Level.WARNING, "Failed to get next result for counts by datasource", ex); - } - } - - /** - * Get the processed results - * - * @return Collection which maps datasource id to a count for the - * number of items found with that datasource id, only - * contains entries for datasources with at least 1 item - * found. - */ - Map getMapOfCounts() { - return Collections.unmodifiableMap(dataSourceObjIdCounts); - } - - } - - } - - /** - * Table model for the Ingest Job table to display ingest jobs for the - * selected datasource. - */ - @Messages({"DataSourceSummaryPanel.IngestJobTableModel.StartTime.header=Start Time", - "DataSourceSummaryPanel.IngestJobTableModel.EndTime.header=End Time", - "DataSourceSummaryPanel.IngestJobTableModel.IngestStatus.header=Ingest Status"}) - private class IngestJobTableModel extends AbstractTableModel { - - private static final long serialVersionUID = 1L; - - private final List columnHeaders = new ArrayList<>(); - - /** - * Create a new IngestJobTableModel - */ - IngestJobTableModel() { - columnHeaders.add(Bundle.DataSourceSummaryPanel_IngestJobTableModel_StartTime_header()); - columnHeaders.add(Bundle.DataSourceSummaryPanel_IngestJobTableModel_EndTime_header()); - columnHeaders.add(Bundle.DataSourceSummaryPanel_IngestJobTableModel_IngestStatus_header()); - } - - @Override - public int getRowCount() { - return ingestJobs.size(); - } - - @Override - public int getColumnCount() { - return columnHeaders.size(); - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - IngestJobInfo currIngestJob = ingestJobs.get(rowIndex); - switch (columnIndex) { - case 0: - return datetimeFormat.format(currIngestJob.getStartDateTime()); - case 1: - Date endDate = currIngestJob.getEndDateTime(); - if (endDate.getTime() == 0) { - return "N/A"; - } - return datetimeFormat.format(currIngestJob.getEndDateTime()); - case 2: - return currIngestJob.getStatus().getDisplayName(); - default: - break; - } - return null; - } - - @Override - public String getColumnName(int column) { - return columnHeaders.get(column); - } - - } - - /** - * Table model for the files table model to display counts of specific file - * types found in the currently selected data source. - */ - @Messages({"DataSourceSummaryPanel.FilesTableModel.type.header=File Type", - "DataSourceSummaryPanel.FilesTableModel.count.header=Count"}) - private class FilesTableModel extends AbstractTableModel { - - private static final long serialVersionUID = 1L; - private final DataSource currentDataSource; - private final List columnHeaders = new ArrayList<>(); - - /** - * Create a FilesTableModel for the speicified datasource. - * - * @param selectedDataSource the datasource which this filesTablemodel - * will represent - */ - FilesTableModel(DataSource selectedDataSource) { - columnHeaders.add(Bundle.DataSourceSummaryPanel_FilesTableModel_type_header()); - columnHeaders.add(Bundle.DataSourceSummaryPanel_FilesTableModel_count_header()); - currentDataSource = selectedDataSource; - } - - @Override - public int getRowCount() { - //should be kept equal to the number of types we are displaying in the tables - return 5; - } - - @Override - public int getColumnCount() { - return columnHeaders.size(); - } - - @Messages({ - "DataSourceSummaryPanel.FilesTableModel.images.row=Images", - "DataSourceSummaryPanel.FilesTableModel.videos.row=Videos", - "DataSourceSummaryPanel.FilesTableModel.audio.row=Audio", - "DataSourceSummaryPanel.FilesTableModel.documents.row=Documents", - "DataSourceSummaryPanel.FilesTableModel.executables.row=Executables" - }) - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - if (columnIndex == 0) { - switch (rowIndex) { - case 0: - return Bundle.DataSourceSummaryPanel_FilesTableModel_images_row(); - case 1: - return Bundle.DataSourceSummaryPanel_FilesTableModel_videos_row(); - case 2: - return Bundle.DataSourceSummaryPanel_FilesTableModel_audio_row(); - case 3: - return Bundle.DataSourceSummaryPanel_FilesTableModel_documents_row(); - case 4: - return Bundle.DataSourceSummaryPanel_FilesTableModel_executables_row(); - default: - break; - } - } else if (columnIndex == 1) { - switch (rowIndex) { - case 0: - return getCountOfFiles(FileTypeUtils.FileTypeCategory.IMAGE.getMediaTypes()); - case 1: - return getCountOfFiles(FileTypeUtils.FileTypeCategory.VIDEO.getMediaTypes()); - case 2: - return getCountOfFiles(FileTypeUtils.FileTypeCategory.AUDIO.getMediaTypes()); - case 3: - return getCountOfFiles(FileTypeUtils.FileTypeCategory.DOCUMENTS.getMediaTypes()); - case 4: - return getCountOfFiles(FileTypeUtils.FileTypeCategory.EXECUTABLE.getMediaTypes()); - default: - break; - } - } - return null; - } - - /** - * Get the number of files in the case database for the current data - * source which have the specified mimetypes. - * - * @param setOfMimeTypes the set of mime types which we are finding the - * number of occurences of - * - * @return a Long value which represents the number of occurrences of - * the specified mime types in the current case for the - * specified data source, null if no count was retrieved - */ - private Long getCountOfFiles(Set setOfMimeTypes) { - if (currentDataSource != null) { - try { - String inClause = String.join("', '", setOfMimeTypes); - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - return skCase.countFilesWhere("data_source_obj_id=" + currentDataSource.getId() - + " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() - + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() - + " AND mime_type IN ('" + inClause + "')" - + " AND name<>''"); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get count of files for specified mime types", ex); - //unable to get count of files for the specified mimetypes cell will be displayed as empty - } - } - return null; - } - - @Override - public String getColumnName(int column) { - return columnHeaders.get(column); - } - - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties new file mode 100644 index 0000000000..2cb4756460 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties @@ -0,0 +1,38 @@ +DataSourceSummaryDialog.closeButton.text=Close +DataSourceSummaryDetailsPanel.displayNameLabel.text=Display Name: +DataSourceSummaryDetailsPanel.originalNameLabel.text=Name: +DataSourceSummaryDetailsPanel.deviceIdLabel.text=Device ID: +DataSourceSummaryDetailsPanel.operatingSystemLabel.text=OS: +DataSourceSummaryDetailsPanel.dataSourceUsageLabel.text=Usage: +DataSourceSummaryDetailsPanel.timeZoneLabel.text=Time Zone: +DataSourceSummaryDetailsPanel.imageTypeLabel.text=Image Type: +DataSourceSummaryDetailsPanel.sizeLabel.text=Size: +DataSourceSummaryDetailsPanel.sectorSizeLabel.text=Sector Size: +DataSourceSummaryDetailsPanel.md5HashLabel.text=MD5: +DataSourceSummaryDetailsPanel.sha1HashLabel.text=SHA1: +DataSourceSummaryDetailsPanel.sha256HashLabel.text=SHA256: +DataSourceSummaryDetailsPanel.filePathsLabel.text=File Paths: +DataSourceSummaryDetailsPanel.displayNameValue.text= +DataSourceSummaryDetailsPanel.originalNameValue.text= +DataSourceSummaryDetailsPanel.deviceIdValue.toolTipText= +DataSourceSummaryDetailsPanel.deviceIdValue.text= +DataSourceSummaryDetailsPanel.dataSourceUsageValue.text= +DataSourceSummaryDetailsPanel.operatingSystemValue.toolTipText= +DataSourceSummaryDetailsPanel.operatingSystemValue.text= +DataSourceSummaryDetailsPanel.timeZoneValue.text= +DataSourceSummaryDetailsPanel.imageTypeValue.toolTipText= +DataSourceSummaryDetailsPanel.imageTypeValue.text= +DataSourceSummaryDetailsPanel.sizeValue.text= +DataSourceSummaryDetailsPanel.sectorSizeValue.text= +DataSourceSummaryDetailsPanel.md5HashValue.toolTipText= +DataSourceSummaryDetailsPanel.md5HashValue.text= +DataSourceSummaryDetailsPanel.sha1HashValue.text= +DataSourceSummaryDetailsPanel.sha256HashValue.text= +DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0= +DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text= +DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text=Acquisition Details: +DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text=Unallocated Space: +DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= +DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type +DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category +DataSourceSummaryCountsPanel.jLabel1.text=Results by Type diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.form new file mode 100644 index 0000000000..bb3b76f532 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.form @@ -0,0 +1,18 @@ + + +
+ + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java new file mode 100644 index 0000000000..4610da61f2 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java @@ -0,0 +1,196 @@ +/* + * 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.casemodule.datasourcesummary; + +import java.awt.EventQueue; +import java.beans.PropertyVetoException; +import javax.swing.ListSelectionModel; +import org.netbeans.swing.outline.DefaultOutlineModel; +import org.netbeans.swing.outline.Outline; +import org.openide.explorer.ExplorerManager; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Observer; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.event.ListSelectionListener; +import org.openide.nodes.Node; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryNode.DataSourceSummaryEntryNode; +import static javax.swing.SwingConstants.RIGHT; +import javax.swing.table.TableColumn; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Panel which allows viewing and selecting of Data Sources and some of their + * related information. + */ +final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerManager.Provider { + + private static final long serialVersionUID = 1L; + private static final Logger logger = Logger.getLogger(DataSourceBrowser.class.getName()); + private final Outline outline; + private final org.openide.explorer.view.OutlineView outlineView; + private final ExplorerManager explorerManager; + private final List dataSourceSummaryList; + private final RightAlignedTableCellRenderer rightAlignedRenderer = new RightAlignedTableCellRenderer(); + + /** + * Creates new form DataSourceBrowser + */ + DataSourceBrowser(Map usageMap, Map fileCountsMap) { + initComponents(); + rightAlignedRenderer.setHorizontalAlignment(RIGHT); + explorerManager = new ExplorerManager(); + outlineView = new org.openide.explorer.view.OutlineView(); + this.setVisible(true); + outlineView.setPropertyColumns( + Bundle.DataSourceSummaryNode_column_type_header(), Bundle.DataSourceSummaryNode_column_type_header(), + Bundle.DataSourceSummaryNode_column_files_header(), Bundle.DataSourceSummaryNode_column_files_header(), + Bundle.DataSourceSummaryNode_column_results_header(), Bundle.DataSourceSummaryNode_column_results_header(), + Bundle.DataSourceSummaryNode_column_tags_header(), Bundle.DataSourceSummaryNode_column_tags_header()); + outline = outlineView.getOutline(); + + outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + dataSourceSummaryList = getDataSourceSummaryList(usageMap, fileCountsMap); + outline.setRootVisible(false); + add(outlineView, java.awt.BorderLayout.CENTER); + explorerManager.setRootContext(new DataSourceSummaryNode(dataSourceSummaryList)); + ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.DataSourceSummaryNode_column_dataSourceName_header()); + for (TableColumn column : Collections.list(outline.getColumnModel().getColumns())) { + if (column.getHeaderValue().toString().equals(Bundle.DataSourceSummaryNode_column_files_header()) + || column.getHeaderValue().toString().equals(Bundle.DataSourceSummaryNode_column_results_header()) + || column.getHeaderValue().toString().equals(Bundle.DataSourceSummaryNode_column_tags_header())) { + column.setCellRenderer(rightAlignedRenderer); + } + } + this.setVisible(true); + } + + /** + * Select the datasource which matches the specified data source object id. + * If the specified id is null or no data source matches the id the first + * data source will be selected instead. + * + * @param dataSourceId the object id of the data source to select, null if + * the first data source should be selected + */ + void selectDataSource(Long dataSourceId) { + EventQueue.invokeLater(() -> { + if (dataSourceId != null) { + for (Node node : explorerManager.getRootContext().getChildren().getNodes()) { + if (node instanceof DataSourceSummaryEntryNode && ((DataSourceSummaryEntryNode) node).getDataSource().getId() == dataSourceId) { + try { + explorerManager.setExploredContextAndSelection(node, new Node[]{node}); + return; + } catch (PropertyVetoException ex) { + logger.log(Level.WARNING, "Failed to select the datasource in the explorer view", ex); //NON-NLS + } + } + } + } + //if no data source was selected and there are data sources to select select the first one + if (explorerManager.getRootContext().getChildren().getNodes().length > 0) { + outline.getSelectionModel().setSelectionInterval(0, 0); + } + }); + } + + /** + * Add the specified observer as an observer of the DataSourceSummaryNode's + * observable. + * + * @param observer the observer which should be added + */ + void addObserver(Observer observer) { + ((DataSourceSummaryNode) explorerManager.getRootContext()).addObserver(observer); + } + + /** + * Get a list of the DataSourceSummary objects representing the information + * to be displayed for each DataSource in this browser. + * + * @return list containing a DataSourceSummary object for each DataSource in + * the current case. + */ + private List getDataSourceSummaryList(Map usageMap, Map fileCountsMap) { + List summaryList = new ArrayList<>(); + + final Map artifactCountsMap = DataSourceInfoUtilities.getCountsOfArtifacts(); + final Map tagCountsMap = DataSourceInfoUtilities.getCountsOfTags(); + try { + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + for (DataSource dataSource : skCase.getDataSources()) { + summaryList.add(new DataSourceSummary(dataSource, usageMap.get(dataSource.getId()), + fileCountsMap.get(dataSource.getId()), artifactCountsMap.get(dataSource.getId()), tagCountsMap.get(dataSource.getId()))); + } + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to datasources or their counts, providing empty results", ex); + } + return summaryList; + } + + /** + * Add a listener to changes in case selections in the table + * + * @param listener the ListSelectionListener to add + */ + void addListSelectionListener(ListSelectionListener listener) { + outline.getSelectionModel().addListSelectionListener(listener); + } + + /** + * Get the DataSource which is currently selected in the ExplorerManager + * + * @return the selected DataSource + */ + DataSource getSelectedDataSource() { + Node selectedNode[] = explorerManager.getSelectedNodes(); + if (selectedNode.length == 1 && selectedNode[0] instanceof DataSourceSummaryEntryNode) { + return ((DataSourceSummaryEntryNode) selectedNode[0]).getDataSource(); + } + return null; + } + + /** + * 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() { + + setLayout(new java.awt.BorderLayout()); + }// //GEN-END:initComponents + + @Override + public ExplorerManager getExplorerManager() { + return explorerManager; + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java new file mode 100644 index 0000000000..e1efb0dc50 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -0,0 +1,366 @@ +/* + * 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.casemodule.datasourcesummary; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Set; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.DataSource; + +/** + * Utilities for getting information about a data source or all data sources + * from the case database. + */ +final class DataSourceInfoUtilities { + + private static final Logger logger = Logger.getLogger(DataSourceInfoUtilities.class.getName()); + + /** + * Get a map containing the TSK_DATA_SOURCE_USAGE description attributes + * associated with each data source in the current case. + * + * @return Collection which maps datasource id to a String which displays a + * comma seperated list of values of data source usage types + * expected to be in the datasource + */ + static Map getDataSourceTypes() { + try { + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + List listOfArtifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE); + Map typeMap = new HashMap<>(); + for (BlackboardArtifact typeArtifact : listOfArtifacts) { + BlackboardAttribute descriptionAttr = typeArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION)); + if (typeArtifact.getDataSource() != null && descriptionAttr != null) { + long dsId = typeArtifact.getDataSource().getId(); + String type = typeMap.get(typeArtifact.getDataSource().getId()); + if (type == null) { + type = descriptionAttr.getValueString(); + } else { + type = type + ", " + descriptionAttr.getValueString(); + } + typeMap.put(dsId, type); + } + } + return typeMap; + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get types of files for all datasources, providing empty results", ex); + return Collections.emptyMap(); + } + } + + /** + * Get a map containing the number of files in each data source in the + * current case. + * + * @return Collection which maps datasource id to a count for the number of + * files in the datasource, will only contain entries for + * datasources which have at least 1 file + */ + static Map getCountsOfFiles() { + try { + final String countFilesQuery = "data_source_obj_id, COUNT(*) AS value" + + " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() + + " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS + return getValuesMap(countFilesQuery); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get counts of files for all datasources, providing empty results", ex); + return Collections.emptyMap(); + } + } + + /** + * Get a map containing the number of artifacts in each data source in the + * current case. + * + * @return Collection which maps datasource id to a count for the number of + * artifacts in the datasource, will only contain entries for + * datasources which have at least 1 artifact + */ + static Map getCountsOfArtifacts() { + try { + final String countArtifactsQuery = "data_source_obj_id, COUNT(*) AS value" + + " FROM blackboard_artifacts WHERE review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID() + + " GROUP BY data_source_obj_id"; //NON-NLS + return getValuesMap(countArtifactsQuery); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get counts of artifacts for all datasources, providing empty results", ex); + return Collections.emptyMap(); + } + } + + /** + * Get a map containing the number of tags which have been applied in each + * data source in the current case. Not necessarily the same as the number + * of items tagged, as an item can have any number of tags. + * + * @return Collection which maps datasource id to a count for the number of + * tags which have been applied in the datasource, will only contain + * entries for datasources which have at least 1 item tagged. + */ + static Map getCountsOfTags() { + try { + final String countFileTagsQuery = "data_source_obj_id, COUNT(*) AS value" + + " FROM content_tags as content_tags, tsk_files as tsk_files" + + " WHERE content_tags.obj_id = tsk_files.obj_id" + + " GROUP BY data_source_obj_id"; //NON-NLS + //new hashmap so it can be modifiable + Map tagCountMap = new HashMap<>(getValuesMap(countFileTagsQuery)); + final String countArtifactTagsQuery = "data_source_obj_id, COUNT(*) AS value" + + " FROM blackboard_artifact_tags as artifact_tags, blackboard_artifacts AS arts" + + " WHERE artifact_tags.artifact_id = arts.artifact_id" + + " GROUP BY data_source_obj_id"; //NON-NLS + //combine the results from the count artifact tags query into the copy of the mapped results from the count file tags query + getValuesMap(countArtifactTagsQuery).forEach((key, value) -> tagCountMap.merge(key, value, (value1, value2) -> value1 + value2)); + return tagCountMap; + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get counts of tags for all datasources, providing empty results", ex); + return Collections.emptyMap(); + } + } + + /** + * Get a map containing the names of operating systems joined in a comma + * seperated list to the Data Source they exist on in the current case. No + * item will exist in the map for data sources which do not contain + * TS_OS_INFO artifacts which have a program name. + * + * @return Collection which maps datasource id to a String which is a comma + * seperated list of Operating system names found on the data + * source. + */ + static Map getOperatingSystems() { + Map osDetailMap = new HashMap<>(); + try { + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + ArrayList osInfoArtifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_INFO); + for (BlackboardArtifact osInfo : osInfoArtifacts) { + BlackboardAttribute programName = osInfo.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME)); + if (programName != null) { + String currentOsString = osDetailMap.get(osInfo.getDataSource().getId()); + if (currentOsString == null || currentOsString.isEmpty()) { + currentOsString = programName.getValueString(); + } else { + currentOsString = currentOsString + ", " + programName.getValueString(); + } + osDetailMap.put(osInfo.getDataSource().getId(), currentOsString); + } + } + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Failed to load OS info artifacts.", ex); + } + return osDetailMap; + } + + /** + * Get the number of files in the case database for the current data source + * which have the specified mimetypes. + * + * @param currentDataSource the data source which we are finding a file + * count + * + * @param setOfMimeTypes the set of mime types which we are finding the + * number of occurences of + * + * @return a Long value which represents the number of occurrences of the + * specified mime types in the current case for the specified data + * source, null if no count was retrieved + */ + static Long getCountOfFilesForMimeTypes(DataSource currentDataSource, Set setOfMimeTypes) { + if (currentDataSource != null) { + try { + String inClause = String.join("', '", setOfMimeTypes); + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + return skCase.countFilesWhere("data_source_obj_id=" + currentDataSource.getId() + + " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() + + " AND mime_type IN ('" + inClause + "')" + + " AND name<>''"); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get count of files for specified mime types", ex); + //unable to get count of files for the specified mimetypes cell will be displayed as empty + } + } + return null; + } + + /** + * Get a map containing the number of unallocated files in each data source + * in the current case. + * + * @return Collection which maps datasource id to a count for the number of + * unallocated files in the datasource, will only contain entries + * for datasources which have at least 1 unallocated file + */ + static Map getCountsOfUnallocatedFiles() { + try { + final String countUnallocatedFilesQuery = "data_source_obj_id, COUNT(*) AS value" + + " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() + + " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue() + + " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS + return getValuesMap(countUnallocatedFilesQuery); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get counts of unallocated files for all datasources, providing empty results", ex); + return Collections.emptyMap(); + } + } + + /** + * Get a map containing the total size of unallocated files in each data + * source in the current case. + * + * @return Collection which maps datasource id to a total size in bytes of + * unallocated files in the datasource, will only contain entries + * for datasources which have at least 1 unallocated file + */ + static Map getSizeOfUnallocatedFiles() { + try { + final String countUnallocatedFilesQuery = "data_source_obj_id, sum(size) AS value" + + " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() + + " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue() + + " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS + return getValuesMap(countUnallocatedFilesQuery); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get size of unallocated files for all datasources, providing empty results", ex); + return Collections.emptyMap(); + } + } + + /** + * Get a map containing the number of directories in each data source in the + * current case. + * + * @return Collection which maps datasource id to a count for the number of + * directories in the datasource, will only contain entries for + * datasources which have at least 1 directory + */ + static Map getCountsOfDirectories() { + try { + final String countDirectoriesQuery = "data_source_obj_id, COUNT(*) AS value" + + " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() + + " AND meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue() + + " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS + return getValuesMap(countDirectoriesQuery); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get counts of directories for all datasources, providing empty results", ex); + return Collections.emptyMap(); + } + } + + /** + * Get a map containing the number of slack files in each data source in the + * current case. + * + * @return Collection which maps datasource id to a count for the number of + * slack files in the datasource, will only contain entries for + * datasources which have at least 1 slack file + */ + static Map getCountsOfSlackFiles() { + try { + final String countSlackFilesQuery = "data_source_obj_id, COUNT(*) AS value" + + " FROM tsk_files WHERE type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType() + + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() + + " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS + return getValuesMap(countSlackFilesQuery); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get counts of slack files for all datasources, providing empty results", ex); + return Collections.emptyMap(); + } + } + + /** + * Get a map containing maps which map artifact type to the number of times + * it exosts in each data source in the current case. + * + * @return Collection which maps datasource id to maps of artifact display + * name to number of occurences in the datasource, will only contain + * entries for artifacts which have at least one occurence in the + * data source. + */ + static Map> getCountsOfArtifactsByType() { + try { + final String countArtifactsQuery = "blackboard_artifacts.data_source_obj_id, blackboard_artifact_types.display_name AS label, COUNT(*) AS value" + + " FROM blackboard_artifacts, blackboard_artifact_types" + + " WHERE blackboard_artifacts.artifact_type_id = blackboard_artifact_types.artifact_type_id" + + " GROUP BY blackboard_artifacts.data_source_obj_id, blackboard_artifact_types.display_name"; + return getLabeledValuesMap(countArtifactsQuery); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get counts of all artifact types for all datasources, providing empty results", ex); + return Collections.emptyMap(); + } + } + + /** + * Helper method to execute a select query with a + * DataSourceLabeledValueCallback. + * + * @param query the portion of the query which should follow the SELECT + * + * @return a map of datasource object IDs to maps of String labels to the + * values associated with them. + * + * @throws TskCoreException + * @throws NoCurrentCaseException + */ + private static Map> getLabeledValuesMap(String query) throws TskCoreException, NoCurrentCaseException { + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + DataSourceLabeledValueCallback callback = new DataSourceLabeledValueCallback(); + skCase.getCaseDbAccessManager().select(query, callback); + return callback.getMapOfLabeledValues(); + } + + /** + * Helper method to execute a select query with a + * DataSourceSingleValueCallback. + * + * @param query the portion of the query which should follow the SELECT + * + * @return a map of datasource object ID to a value of type Long + * + * @throws TskCoreException + * @throws NoCurrentCaseException + */ + private static Map getValuesMap(String query) throws TskCoreException, NoCurrentCaseException { + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + DataSourceSingleValueCallback callback = new DataSourceSingleValueCallback(); + skCase.getCaseDbAccessManager().select(query, callback); + return callback.getMapOfValues(); + } + + /** + * Empty private constructor + */ + private DataSourceInfoUtilities() { + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceLabeledValueCallback.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceLabeledValueCallback.java new file mode 100644 index 0000000000..3edd92c8b0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceLabeledValueCallback.java @@ -0,0 +1,71 @@ +/* + * 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.casemodule.datasourcesummary; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.sleuthkit.datamodel.CaseDbAccessManager; + +/** + * Callback which gets a map of datasource object IDs to maps of String labels + * to the values associated with them, given a query which should select a + * 'data_source_obj_id', a 'label', and a 'value'. + */ +class DataSourceLabeledValueCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + + private static final Logger logger = Logger.getLogger(DataSourceSingleValueCallback.class.getName()); + private final Map> dataSourceObjIdLabeledValues = new HashMap<>(); + + @Override + public void process(ResultSet rs) { + try { + while (rs.next()) { + try { + Long dataSourceObjId = rs.getLong("data_source_obj_id"); + String labelForValue = rs.getString("label"); + Long value = rs.getLong("value"); + Map labelsMap = dataSourceObjIdLabeledValues.get(dataSourceObjId) == null ? new HashMap<>() : dataSourceObjIdLabeledValues.get(dataSourceObjId); + labelsMap.put(labelForValue, value); + dataSourceObjIdLabeledValues.put(dataSourceObjId, labelsMap); + } catch (SQLException ex) { + logger.log(Level.WARNING, "Unable to get data_source_obj_id or value from result set", ex); + } + } + } catch (SQLException ex) { + logger.log(Level.WARNING, "Failed to get next result for valuess by datasource", ex); + } + } + + /** + * Get the processed results + * + * @return Collection which maps datasource object IDs to maps of String + * labels to the values associated with them only contains entries + * for data sources with at least 1 item found for any label. + */ + Map> getMapOfLabeledValues() { + return Collections.unmodifiableMap(dataSourceObjIdLabeledValues); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSingleValueCallback.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSingleValueCallback.java new file mode 100644 index 0000000000..241b74c87e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSingleValueCallback.java @@ -0,0 +1,66 @@ +/* + * 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.casemodule.datasourcesummary; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.sleuthkit.datamodel.CaseDbAccessManager; + +/** + * Get the map of Data Source ID to a value found related to that data source + * query which selects data_source_obj_id and value which is grouped by that + * data source object id. + */ +class DataSourceSingleValueCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + + private static final Logger logger = Logger.getLogger(DataSourceSingleValueCallback.class.getName()); + private final Map dataSourceObjIdValues = new HashMap<>(); + + @Override + public void process(ResultSet rs) { + try { + while (rs.next()) { + try { + dataSourceObjIdValues.put(rs.getLong("data_source_obj_id"), rs.getLong("value")); + } catch (SQLException ex) { + logger.log(Level.WARNING, "Unable to get data_source_obj_id or value from result set", ex); + } + } + } catch (SQLException ex) { + logger.log(Level.WARNING, "Failed to get next result for valuess by datasource", ex); + } + } + + /** + * Get the processed results + * + * @return Collection which maps datasource id to a value related to the + * datasource id, only contains entries for datasources with at + * least 1 item found. + */ + Map getMapOfValues() { + return Collections.unmodifiableMap(dataSourceObjIdValues); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java new file mode 100644 index 0000000000..1aa55d95b5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java @@ -0,0 +1,98 @@ +/* + * 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.casemodule.datasourcesummary; + +import org.sleuthkit.datamodel.DataSource; + +/** + * Wrapper object for a DataSource and the information associated with it. + * + */ +class DataSourceSummary { + + private final DataSource dataSource; + private final String type; + private final long filesCount; + private final long resultsCount; + private final long tagsCount; + + /** + * Construct a new DataSourceSummary object + * + * @param dSource the DataSource being wrapped by this object + * @param typeValue the Type sting to be associated with this + * DataSource + * @param numberOfFiles the number of files found in this DataSource + * @param numberOfResults the number of artifacts found in this DataSource + * @param numberOfTags the number of tagged content objects in this + * DataSource + */ + DataSourceSummary(DataSource dSource, String typeValue, Long numberOfFiles, Long numberOfResults, Long numberOfTags) { + dataSource = dSource; + type = typeValue == null ? "" : typeValue; + filesCount = numberOfFiles == null ? 0 : numberOfFiles; + resultsCount = numberOfResults == null ? 0 : numberOfResults; + tagsCount = numberOfTags == null ? 0 : numberOfTags; + } + + /** + * Get the DataSource + * + * @return the dataSource + */ + DataSource getDataSource() { + return dataSource; + } + + /** + * Get the type of this DataSource + * + * @return the type + */ + String getType() { + return type; + } + + /** + * Get the number of files in this DataSource + * + * @return the filesCount + */ + long getFilesCount() { + return filesCount; + } + + /** + * Get the number of artifacts in this DataSource + * + * @return the resultsCount + */ + long getResultsCount() { + return resultsCount; + } + + /** + * Get the number of tagged content objects in this DataSource + * + * @return the tagsCount + */ + long getTagsCount() { + return tagsCount; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryAction.java similarity index 90% rename from Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryAction.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryAction.java index 5dc3035867..7514d61599 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryAction.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.casemodule.datasourceSummary; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; @@ -29,11 +29,12 @@ import org.openide.util.NbBundle.Messages; import org.openide.util.actions.CallableSystemAction; import org.sleuthkit.autopsy.casemodule.Case; -@ActionID(category = "Case", id = "org.sleuthkit.autopsy.casemodule.datasourceSummary.DataSourceSummaryAction") +@ActionID(category = "Case", id = "org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryAction") @ActionRegistration(displayName = "#CTL_DataSourceSummaryAction", lazy = false) @Messages({"CTL_DataSourceSummaryAction=Data Source Summary"}) /** - * DataSourceSummaryAction action for the Case menu to activate a ViewSummaryInformationAction selecting the first data source. + * DataSourceSummaryAction action for the Case menu to activate a + * ViewSummaryInformationAction selecting the first data source. */ public class DataSourceSummaryAction extends CallableSystemAction { @@ -66,4 +67,9 @@ public class DataSourceSummaryAction extends CallableSystemAction { public HelpCtx getHelpCtx() { return HelpCtx.DEFAULT_HELP; } + + @Override + public boolean asynchronous() { + return false; + } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.form new file mode 100644 index 0000000000..232c6889ba --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.form @@ -0,0 +1,139 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java new file mode 100644 index 0000000000..c04926d84f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java @@ -0,0 +1,395 @@ +/* + * 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.casemodule.datasourcesummary; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import javax.swing.JLabel; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.datamodel.utils.FileTypeUtils; +import org.sleuthkit.datamodel.DataSource; + +/** + * Panel for displaying summary information on the known files present in the + * specified DataSource + */ +class DataSourceSummaryCountsPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + private FilesByMimeTypeTableModel filesByMimeTypeTableModel = new FilesByMimeTypeTableModel(null); + private FilesByCategoryTableModel filesByCategoryTableModel = new FilesByCategoryTableModel(null); + private static final Logger logger = Logger.getLogger(DataSourceSummaryCountsPanel.class.getName()); + private final Map allFilesCountsMap; + private final Map slackFilesCountsMap; + private final Map directoriesCountsMap; + private final Map unallocatedFilesCountsMap; + private final Map> artifactsByTypeCountsMap; + private final DefaultTableCellRenderer rightAlignedRenderer = new DefaultTableCellRenderer(); + + /** + * Creates new form DataSourceSummaryCountsPanel + */ + DataSourceSummaryCountsPanel(Map fileCountsMap) { + this.allFilesCountsMap = fileCountsMap; + this.slackFilesCountsMap = DataSourceInfoUtilities.getCountsOfSlackFiles(); + this.directoriesCountsMap = DataSourceInfoUtilities.getCountsOfDirectories(); + this.unallocatedFilesCountsMap = DataSourceInfoUtilities.getCountsOfUnallocatedFiles(); + this.artifactsByTypeCountsMap = DataSourceInfoUtilities.getCountsOfArtifactsByType(); + rightAlignedRenderer.setHorizontalAlignment(JLabel.RIGHT); + initComponents(); + fileCountsByMimeTypeTable.getTableHeader().setReorderingAllowed(false); + fileCountsByCategoryTable.getTableHeader().setReorderingAllowed(false); + } + + /** + * Specify the DataSource to display file information for + * + * @param selectedDataSource the DataSource to display file information for + */ + void updateCountsTableData(DataSource selectedDataSource) { + filesByMimeTypeTableModel = new FilesByMimeTypeTableModel(selectedDataSource); + fileCountsByMimeTypeTable.setModel(filesByMimeTypeTableModel); + fileCountsByMimeTypeTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); + fileCountsByMimeTypeTable.getColumnModel().getColumn(0).setPreferredWidth(130); + filesByCategoryTableModel = new FilesByCategoryTableModel(selectedDataSource); + fileCountsByCategoryTable.setModel(filesByCategoryTableModel); + fileCountsByCategoryTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); + fileCountsByCategoryTable.getColumnModel().getColumn(0).setPreferredWidth(130); + updateArtifactCounts(selectedDataSource); + this.repaint(); + } + + /** + * Helper method to update the artifact specific counts by clearing the + * table and adding counts for the artifacts which exist in the selected + * data source. + * + * @param selectedDataSource the data source to display artifact counts for + */ + private void updateArtifactCounts(DataSource selectedDataSource) { + ((DefaultTableModel) artifactCountsTable.getModel()).setRowCount(0); + if (selectedDataSource != null && artifactsByTypeCountsMap.get(selectedDataSource.getId()) != null) { + Map artifactCounts = artifactsByTypeCountsMap.get(selectedDataSource.getId()); + for (String key : artifactCounts.keySet()) { + ((DefaultTableModel) artifactCountsTable.getModel()).addRow(new Object[]{key, artifactCounts.get(key)}); + } + } + artifactCountsTable.getColumnModel().getColumn(0).setPreferredWidth(230); + artifactCountsTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); + } + + /** + * 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() { + + fileCountsByMimeTypeScrollPane = new javax.swing.JScrollPane(); + fileCountsByMimeTypeTable = new javax.swing.JTable(); + byMimeTypeLabel = new javax.swing.JLabel(); + fileCountsByCategoryScrollPane = new javax.swing.JScrollPane(); + fileCountsByCategoryTable = new javax.swing.JTable(); + byCategoryLabel = new javax.swing.JLabel(); + jLabel1 = new javax.swing.JLabel(); + artifactCountsScrollPane = new javax.swing.JScrollPane(); + artifactCountsTable = new javax.swing.JTable(); + + fileCountsByMimeTypeTable.setModel(filesByMimeTypeTableModel); + fileCountsByMimeTypeScrollPane.setViewportView(fileCountsByMimeTypeTable); + + org.openide.awt.Mnemonics.setLocalizedText(byMimeTypeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.byMimeTypeLabel.text")); // NOI18N + + fileCountsByCategoryTable.setModel(filesByCategoryTableModel); + 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(jLabel1, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.jLabel1.text")); // NOI18N + + artifactCountsTable.setAutoCreateRowSorter(true); + artifactCountsTable.setModel(new javax.swing.table.DefaultTableModel( + new Object [][] { + + }, + new String [] { + "Result Type", "Count" + } + ) { + boolean[] canEdit = new boolean [] { + false, false + }; + + public boolean isCellEditable(int rowIndex, int columnIndex) { + return canEdit [columnIndex]; + } + }); + artifactCountsScrollPane.setViewportView(artifactCountsTable); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(fileCountsByMimeTypeScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 140, Short.MAX_VALUE) + .addComponent(byMimeTypeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(byCategoryLabel) + .addComponent(fileCountsByCategoryScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(artifactCountsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 244, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {fileCountsByCategoryScrollPane, fileCountsByMimeTypeScrollPane}); + + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(byMimeTypeLabel) + .addComponent(byCategoryLabel) + .addComponent(jLabel1)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(artifactCountsScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(fileCountsByMimeTypeScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(fileCountsByCategoryScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 86, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) + ); + + layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {fileCountsByCategoryScrollPane, fileCountsByMimeTypeScrollPane}); + + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JScrollPane artifactCountsScrollPane; + private javax.swing.JTable artifactCountsTable; + private javax.swing.JLabel byCategoryLabel; + private javax.swing.JLabel byMimeTypeLabel; + private javax.swing.JScrollPane fileCountsByCategoryScrollPane; + private javax.swing.JTable fileCountsByCategoryTable; + private javax.swing.JScrollPane fileCountsByMimeTypeScrollPane; + private javax.swing.JTable fileCountsByMimeTypeTable; + private javax.swing.JLabel jLabel1; + // End of variables declaration//GEN-END:variables + + /** + * Table model for the files table model to display counts of specific file + * types by mime type found in the currently selected data source. + */ + @Messages({"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.type.header=File Type", + "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.count.header=Count"}) + private class FilesByMimeTypeTableModel extends AbstractTableModel { + + private static final long serialVersionUID = 1L; + private final DataSource currentDataSource; + private final List columnHeaders = new ArrayList<>(); + private static final int IMAGES_ROW_INDEX = 0; + private static final int VIDEOS_ROW_INDEX = 1; + private static final int AUDIO_ROW_INDEX = 2; + private static final int DOCUMENTS_ROW_INDEX = 3; + private static final int EXECUTABLES_ROW_INDEX = 4; + + /** + * Create a FilesByMimeTypeTableModel for the speicified datasource. + * + * @param selectedDataSource the datasource which this + * FilesByMimeTypeTablemodel will represent + */ + FilesByMimeTypeTableModel(DataSource selectedDataSource) { + columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_type_header()); + columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_count_header()); + currentDataSource = selectedDataSource; + } + + @Override + public int getRowCount() { + //should be kept equal to the number of types we are displaying in the tables + return 5; + } + + @Override + public int getColumnCount() { + return columnHeaders.size(); + } + + @Messages({ + "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images", + "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos", + "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio", + "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents", + "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables" + }) + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + if (columnIndex == 0) { + switch (rowIndex) { + case IMAGES_ROW_INDEX: + return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_images_row(); + case VIDEOS_ROW_INDEX: + return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_videos_row(); + case AUDIO_ROW_INDEX: + return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_audio_row(); + case DOCUMENTS_ROW_INDEX: + return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_documents_row(); + case EXECUTABLES_ROW_INDEX: + return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_executables_row(); + default: + break; + } + } else if (columnIndex == 1) { + switch (rowIndex) { + case 0: + return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.IMAGE.getMediaTypes()); + case 1: + return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.VIDEO.getMediaTypes()); + case 2: + return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.AUDIO.getMediaTypes()); + case 3: + return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.DOCUMENTS.getMediaTypes()); + case 4: + return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.EXECUTABLE.getMediaTypes()); + default: + break; + } + } + return null; + } + + @Override + public String getColumnName(int column) { + return columnHeaders.get(column); + } + } + + /** + * Table model for the files table model to display counts of specific file + * types by category found in the currently selected data source. + */ + @Messages({"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type", + "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count"}) + private class FilesByCategoryTableModel extends AbstractTableModel { + + private static final long serialVersionUID = 1L; + private final DataSource currentDataSource; + private final List columnHeaders = new ArrayList<>(); + private static final int ALL_FILES_ROW_INDEX = 0; + private static final int ALLOCATED_FILES_ROW_INDEX = 1; + private static final int UNALLOCATED_FILES_ROW_INDEX = 2; + private static final int SLACK_FILES_ROW_INDEX = 3; + private static final int DIRECTORIES_ROW_INDEX = 4; + /** + * Create a FilesByCategoryTableModel for the speicified datasource. + * + * @param selectedDataSource the datasource which this + * FilesByCategoryTablemodel will represent + */ + FilesByCategoryTableModel(DataSource selectedDataSource) { + columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_type_header()); + columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_count_header()); + currentDataSource = selectedDataSource; + } + + @Override + public int getRowCount() { + //should be kept equal to the number of types we are displaying in the tables + return 5; + } + + @Override + public int getColumnCount() { + return columnHeaders.size(); + } + + @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" + }) + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + if (columnIndex == 0) { + switch (rowIndex) { + case ALL_FILES_ROW_INDEX: + return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_all_row(); + case ALLOCATED_FILES_ROW_INDEX: + return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_allocated_row(); + case UNALLOCATED_FILES_ROW_INDEX: + return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_unallocated_row(); + case SLACK_FILES_ROW_INDEX: + return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_slack_row(); + case DIRECTORIES_ROW_INDEX: + return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_directory_row(); + default: + break; + } + } else if (columnIndex == 1 && currentDataSource != null) { + switch (rowIndex) { + case 0: + return allFilesCountsMap.get(currentDataSource.getId()) == null ? 0 : allFilesCountsMap.get(currentDataSource.getId()); + case 1: + //All files should be either allocated or unallocated as dir_flags only has two values so any file that isn't unallocated is allocated + Long unallocatedFilesCount = unallocatedFilesCountsMap.get(currentDataSource.getId()); + Long allFilesCount = allFilesCountsMap.get(currentDataSource.getId()); + if (allFilesCount == null) { + return 0; + } else if (unallocatedFilesCount == null) { + return allFilesCount; + } else { + return allFilesCount - unallocatedFilesCount; + } + case 2: + return unallocatedFilesCountsMap.get(currentDataSource.getId()) == null ? 0 : unallocatedFilesCountsMap.get(currentDataSource.getId()); + case 3: + return slackFilesCountsMap.get(currentDataSource.getId()) == null ? 0 : slackFilesCountsMap.get(currentDataSource.getId()); + case 4: + return directoriesCountsMap.get(currentDataSource.getId()) == null ? 0 : directoriesCountsMap.get(currentDataSource.getId()); + default: + break; + } + } + return null; + } + + @Override + public String getColumnName(int column) { + return columnHeaders.get(column); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.form new file mode 100644 index 0000000000..d45990f66d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.form @@ -0,0 +1,498 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + <ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + + + + + + + + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java new file mode 100644 index 0000000000..7702a0a20b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java @@ -0,0 +1,699 @@ +/* + * 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.casemodule.datasourcesummary; + +import java.text.DecimalFormat; +import java.util.Map; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.table.DefaultTableModel; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.Image; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Panel to display additional details associated with a specific DataSource + */ +class DataSourceSummaryDetailsPanel extends javax.swing.JPanel { + + //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 Map osDetailMap = new HashMap<>(); + private static final Integer SIZE_COVERSION_CONSTANT = 1000; + private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##"); + private final Map unallocatedFilesSizeMap; + private final Map usageMap; + private static final Logger logger = Logger.getLogger(DataSourceSummaryDetailsPanel.class.getName()); + + /** + * Creates new form DataSourceSummaryDetailsPanel + */ + @Messages({"DataSourceSummaryDetailsPanel.getDataSources.error.text=Failed to get the list of datasources for the current case.", + "DataSourceSummaryDetailsPanel.getDataSources.error.title=Load Failure"}) + DataSourceSummaryDetailsPanel(Map usageMap) { + initComponents(); + this.usageMap = usageMap; + this.unallocatedFilesSizeMap = DataSourceInfoUtilities.getSizeOfUnallocatedFiles(); + osDetailMap = DataSourceInfoUtilities.getOperatingSystems(); + } + + /** + * Update which DataSource this panel should display details about + * + * @param selectedDataSource the DataSource to display details about. + */ + void updateDetailsPanelData(DataSource selectedDataSource) { + clearTableValues(); + if (selectedDataSource != null) { + String sizeString = ""; + String sectorSizeString = ""; + String md5String = ""; + String sha1String = ""; + String sha256String = ""; + String acquisitionDetailsString = ""; + String imageTypeString = ""; + String[] filePaths = new String[0]; + String osDetailString = osDetailMap.get(selectedDataSource.getId()) == null ? "" : osDetailMap.get(selectedDataSource.getId()); + String dataSourceTypeString = usageMap.get(selectedDataSource.getId()) == null ? "" : usageMap.get(selectedDataSource.getId()); + try { + acquisitionDetailsString = selectedDataSource.getAcquisitionDetails(); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get aquisition details for selected data source", ex); + } + if (selectedDataSource instanceof Image) { + imageTypeString = ((Image) selectedDataSource).getType().getName(); + filePaths = ((Image) selectedDataSource).getPaths(); + sizeString = getSizeString(selectedDataSource.getSize()); + sectorSizeString = getSizeString(((Image) selectedDataSource).getSsize()); + try { + //older databases may have null as the hash values + md5String = ((Image) selectedDataSource).getMd5(); + if (md5String == null) { + md5String = ""; + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get MD5 for selected data source", ex); + } + try { + sha1String = ((Image) selectedDataSource).getSha1(); + if (sha1String == null) { + sha1String = ""; + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get SHA1 for selected data source", ex); + } + try { + sha256String = ((Image) selectedDataSource).getSha256(); + if (sha256String == null) { + sha256String = ""; + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get SHA256 for selected data source", ex); + } + } + displayNameValue.setText(selectedDataSource.getName()); + originalNameValue.setText(selectedDataSource.getName()); + deviceIdValue.setText(selectedDataSource.getDeviceId()); + dataSourceUsageValue.setText(dataSourceTypeString); + operatingSystemValue.setText(osDetailString); + timeZoneValue.setText(selectedDataSource.getTimeZone()); + acquisitionDetailsTextArea.setText(acquisitionDetailsString); + imageTypeValue.setText(imageTypeString); + sizeValue.setText(sizeString); + unallocatedSizeValue.setText(getSizeString(unallocatedFilesSizeMap.get(selectedDataSource.getId()))); + sectorSizeValue.setText(sectorSizeString); + md5HashValue.setText(md5String); + sha1HashValue.setText(sha1String); + sha256HashValue.setText(sha256String); + for (String path : filePaths) { + ((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path}); + } + } + updateFieldVisibility(); + this.repaint(); + } + + /** + * 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 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. + */ + private void updateFieldVisibility() { + displayNameValue.setVisible(!displayNameValue.getText().isEmpty()); + displayNameLabel.setVisible(!displayNameValue.getText().isEmpty()); + originalNameValue.setVisible(!originalNameValue.getText().isEmpty()); + originalNameLabel.setVisible(!originalNameValue.getText().isEmpty()); + deviceIdValue.setVisible(!deviceIdValue.getText().isEmpty()); + deviceIdLabel.setVisible(!deviceIdValue.getText().isEmpty()); + dataSourceUsageValue.setVisible(!dataSourceUsageValue.getText().isEmpty()); + dataSourceUsageLabel.setVisible(!dataSourceUsageValue.getText().isEmpty()); + operatingSystemValue.setVisible(!operatingSystemValue.getText().isEmpty()); + operatingSystemLabel.setVisible(!operatingSystemValue.getText().isEmpty()); + timeZoneValue.setVisible(!timeZoneValue.getText().isEmpty()); + timeZoneLabel.setVisible(!timeZoneValue.getText().isEmpty()); + acquisitionDetailsTextArea.setVisible(!acquisitionDetailsTextArea.getText().isEmpty()); + acquisitionDetailsLabel.setVisible(!acquisitionDetailsTextArea.getText().isEmpty()); + acquisitionDetailsScrollPane.setVisible(!acquisitionDetailsTextArea.getText().isEmpty()); + imageTypeValue.setVisible(!imageTypeValue.getText().isEmpty()); + imageTypeLabel.setVisible(!imageTypeValue.getText().isEmpty()); + sizeValue.setVisible(!sizeValue.getText().isEmpty()); + sizeLabel.setVisible(!sizeValue.getText().isEmpty()); + sectorSizeValue.setVisible(!sectorSizeValue.getText().isEmpty()); + sectorSizeLabel.setVisible(!sectorSizeValue.getText().isEmpty()); + md5HashValue.setVisible(!md5HashValue.getText().isEmpty()); + md5HashLabel.setVisible(!md5HashValue.getText().isEmpty()); + sha1HashValue.setVisible(!sha1HashValue.getText().isEmpty()); + sha1HashLabel.setVisible(!sha1HashValue.getText().isEmpty()); + sha256HashValue.setVisible(!sha256HashValue.getText().isEmpty()); + sha256HashLabel.setVisible(!sha256HashValue.getText().isEmpty()); + unallocatedSizeValue.setVisible(!unallocatedSizeValue.getText().isEmpty()); + unallocatedSizeLabel.setVisible(!unallocatedSizeValue.getText().isEmpty()); + filePathsTable.setVisible(filePathsTable.getRowCount() > 0); + filePathsLabel.setVisible(filePathsTable.getRowCount() > 0); + filePathsScrollPane.setVisible(filePathsTable.getRowCount() > 0); + } + + /** + * Set the contents of all fields to be empty. + */ + private void clearTableValues() { + displayNameValue.setText(""); + originalNameValue.setText(""); + deviceIdValue.setText(""); + dataSourceUsageValue.setText(""); + operatingSystemValue.setText(""); + timeZoneValue.setText(""); + acquisitionDetailsTextArea.setText(""); + imageTypeValue.setText(""); + sizeValue.setText(""); + sectorSizeValue.setText(""); + md5HashValue.setText(""); + sha1HashValue.setText(""); + sha256HashValue.setText(""); + unallocatedSizeValue.setText(""); + ((DefaultTableModel) filePathsTable.getModel()).setRowCount(0); + } + + /** + * 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() { + java.awt.GridBagConstraints gridBagConstraints; + + jScrollPane1 = new javax.swing.JScrollPane(); + jPanel1 = new javax.swing.JPanel(); + operatingSystemLabel = new javax.swing.JLabel(); + displayNameLabel = new javax.swing.JLabel(); + originalNameLabel = new javax.swing.JLabel(); + sha1HashValue = new javax.swing.JLabel(); + operatingSystemValue = new javax.swing.JLabel(); + displayNameValue = new javax.swing.JLabel(); + sha256HashValue = new javax.swing.JLabel(); + originalNameValue = new javax.swing.JLabel(); + deviceIdValue = new javax.swing.JLabel(); + filePathsScrollPane = new javax.swing.JScrollPane(); + filePathsTable = new javax.swing.JTable(); + dataSourceUsageValue = new javax.swing.JLabel(); + timeZoneValue = new javax.swing.JLabel(); + imageTypeValue = new javax.swing.JLabel(); + md5HashValue = new javax.swing.JLabel(); + sectorSizeValue = new javax.swing.JLabel(); + sizeValue = new javax.swing.JLabel(); + filePathsLabel = new javax.swing.JLabel(); + sha256HashLabel = new javax.swing.JLabel(); + sha1HashLabel = new javax.swing.JLabel(); + md5HashLabel = new javax.swing.JLabel(); + sectorSizeLabel = new javax.swing.JLabel(); + sizeLabel = new javax.swing.JLabel(); + imageTypeLabel = new javax.swing.JLabel(); + acquisitionDetailsLabel = new javax.swing.JLabel(); + timeZoneLabel = new javax.swing.JLabel(); + dataSourceUsageLabel = new javax.swing.JLabel(); + deviceIdLabel = new javax.swing.JLabel(); + acquisitionDetailsScrollPane = new javax.swing.JScrollPane(); + acquisitionDetailsTextArea = new javax.swing.JTextArea(); + filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 32767)); + filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 32767)); + unallocatedSizeLabel = new javax.swing.JLabel(); + unallocatedSizeValue = new javax.swing.JLabel(); + + jPanel1.setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(operatingSystemLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.operatingSystemLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 4); + jPanel1.add(operatingSystemLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(displayNameLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.displayNameLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(10, 10, 0, 4); + jPanel1.add(displayNameLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(originalNameLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.originalNameLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 4); + jPanel1.add(originalNameLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(sha1HashValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sha1HashValue.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 12; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10); + jPanel1.add(sha1HashValue, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(operatingSystemValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.operatingSystemValue.text")); // NOI18N + operatingSystemValue.setToolTipText(org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.operatingSystemValue.toolTipText")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 4; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10); + jPanel1.add(operatingSystemValue, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(displayNameValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.displayNameValue.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(10, 0, 0, 10); + jPanel1.add(displayNameValue, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(sha256HashValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sha256HashValue.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 13; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 6, 10); + jPanel1.add(sha256HashValue, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(originalNameValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.originalNameValue.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10); + jPanel1.add(originalNameValue, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(deviceIdValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.deviceIdValue.text")); // NOI18N + deviceIdValue.setToolTipText(org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.deviceIdValue.toolTipText")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 6, 10); + jPanel1.add(deviceIdValue, gridBagConstraints); + + filePathsScrollPane.setPreferredSize(new java.awt.Dimension(80, 50)); + + filePathsTable.setModel(new javax.swing.table.DefaultTableModel( + new Object [][] { + + }, + new String [] { + "" + } + ) { + boolean[] canEdit = new boolean [] { + false + }; + + public boolean isCellEditable(int rowIndex, int columnIndex) { + return canEdit [columnIndex]; + } + }); + filePathsTable.setTableHeader(null); + filePathsScrollPane.setViewportView(filePathsTable); + if (filePathsTable.getColumnModel().getColumnCount() > 0) { + filePathsTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0")); // NOI18N + } + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 14; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.weighty = 1.2; + gridBagConstraints.insets = new java.awt.Insets(6, 0, 10, 10); + jPanel1.add(filePathsScrollPane, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(dataSourceUsageValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.dataSourceUsageValue.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 3; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 10); + jPanel1.add(dataSourceUsageValue, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(timeZoneValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.timeZoneValue.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 5; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 6, 10); + jPanel1.add(timeZoneValue, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(imageTypeValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.imageTypeValue.text")); // NOI18N + imageTypeValue.setToolTipText(org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.imageTypeValue.toolTipText")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 7; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 10); + jPanel1.add(imageTypeValue, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(md5HashValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.md5HashValue.text")); // NOI18N + md5HashValue.setToolTipText(org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.md5HashValue.toolTipText")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 11; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10); + jPanel1.add(md5HashValue, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(sectorSizeValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sectorSizeValue.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 10; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10); + jPanel1.add(sectorSizeValue, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(sizeValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sizeValue.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 8; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10); + jPanel1.add(sizeValue, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(filePathsLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.filePathsLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 14; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weighty = 1.2; + gridBagConstraints.insets = new java.awt.Insets(6, 10, 10, 4); + jPanel1.add(filePathsLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(sha256HashLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sha256HashLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 13; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 6, 4); + jPanel1.add(sha256HashLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(sha1HashLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sha1HashLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 12; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 4); + jPanel1.add(sha1HashLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(md5HashLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.md5HashLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 11; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 4); + jPanel1.add(md5HashLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(sectorSizeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sectorSizeLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 10; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 4); + jPanel1.add(sectorSizeLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(sizeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sizeLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 8; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 4); + jPanel1.add(sizeLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(imageTypeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.imageTypeLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 7; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(6, 10, 0, 4); + jPanel1.add(imageTypeLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(acquisitionDetailsLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 6; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weighty = 0.6; + gridBagConstraints.insets = new java.awt.Insets(6, 10, 6, 4); + jPanel1.add(acquisitionDetailsLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(timeZoneLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.timeZoneLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 5; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 6, 4); + jPanel1.add(timeZoneLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(dataSourceUsageLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.dataSourceUsageLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(6, 10, 0, 4); + jPanel1.add(dataSourceUsageLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(deviceIdLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.deviceIdLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 6, 4); + jPanel1.add(deviceIdLabel, gridBagConstraints); + + acquisitionDetailsTextArea.setEditable(false); + acquisitionDetailsTextArea.setBackground(javax.swing.UIManager.getDefaults().getColor("TextArea.disabledBackground")); + acquisitionDetailsTextArea.setColumns(20); + acquisitionDetailsTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N + acquisitionDetailsTextArea.setRows(4); + acquisitionDetailsTextArea.setText(org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text")); // NOI18N + acquisitionDetailsTextArea.setBorder(null); + acquisitionDetailsScrollPane.setViewportView(acquisitionDetailsTextArea); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 6; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.weighty = 0.6; + gridBagConstraints.insets = new java.awt.Insets(6, 0, 6, 10); + jPanel1.add(acquisitionDetailsScrollPane, gridBagConstraints); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 15; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weighty = 0.1; + jPanel1.add(filler1, gridBagConstraints); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 15; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weighty = 0.1; + jPanel1.add(filler2, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(unallocatedSizeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 9; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 4); + jPanel1.add(unallocatedSizeLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(unallocatedSizeValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.unallocatedSizeValue.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 9; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10); + jPanel1.add(unallocatedSizeValue, gridBagConstraints); + + jScrollPane1.setViewportView(jPanel1); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) + .addGap(0, 0, 0)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel acquisitionDetailsLabel; + private javax.swing.JScrollPane acquisitionDetailsScrollPane; + private javax.swing.JTextArea acquisitionDetailsTextArea; + private javax.swing.JLabel dataSourceUsageLabel; + private javax.swing.JLabel dataSourceUsageValue; + private javax.swing.JLabel deviceIdLabel; + private javax.swing.JLabel deviceIdValue; + private javax.swing.JLabel displayNameLabel; + private javax.swing.JLabel displayNameValue; + private javax.swing.JLabel filePathsLabel; + private javax.swing.JScrollPane filePathsScrollPane; + private javax.swing.JTable filePathsTable; + private javax.swing.Box.Filler filler1; + private javax.swing.Box.Filler filler2; + private javax.swing.JLabel imageTypeLabel; + private javax.swing.JLabel imageTypeValue; + private javax.swing.JPanel jPanel1; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JLabel md5HashLabel; + private javax.swing.JLabel md5HashValue; + private javax.swing.JLabel operatingSystemLabel; + private javax.swing.JLabel operatingSystemValue; + private javax.swing.JLabel originalNameLabel; + private javax.swing.JLabel originalNameValue; + private javax.swing.JLabel sectorSizeLabel; + private javax.swing.JLabel sectorSizeValue; + private javax.swing.JLabel sha1HashLabel; + private javax.swing.JLabel sha1HashValue; + private javax.swing.JLabel sha256HashLabel; + private javax.swing.JLabel sha256HashValue; + private javax.swing.JLabel sizeLabel; + private javax.swing.JLabel sizeValue; + private javax.swing.JLabel timeZoneLabel; + private javax.swing.JLabel timeZoneValue; + private javax.swing.JLabel unallocatedSizeLabel; + private javax.swing.JLabel unallocatedSizeValue; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.form new file mode 100644 index 0000000000..72c45cb5a7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.form @@ -0,0 +1,82 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java new file mode 100644 index 0000000000..87e6640e13 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java @@ -0,0 +1,161 @@ +/* + * 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.casemodule.datasourcesummary; + +import java.awt.Frame; +import java.util.Map; +import java.util.Observable; +import java.util.Observer; +import java.util.logging.Logger; +import javax.swing.event.ListSelectionEvent; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; +import org.sleuthkit.datamodel.DataSource; + +/** + * Dialog for displaying the Data Sources Summary information + */ +final class DataSourceSummaryDialog extends javax.swing.JDialog implements Observer { + + private static final long serialVersionUID = 1L; + private final DataSourceSummaryCountsPanel countsPanel; + private final DataSourceSummaryDetailsPanel detailsPanel; + private final DataSourceBrowser dataSourcesPanel; + private final IngestJobInfoPanel ingestHistoryPanel; + private static final Logger logger = Logger.getLogger(DataSourceSummaryDialog.class.getName()); + + /** + * Creates new form DataSourceSummaryDialog for displaying a summary of the + * data sources for the fcurrent case and the contents found for each + * datasource. + */ + @Messages({ + "DataSourceSummaryDialog.window.title=Data Sources Summary", + "DataSourceSummaryDialog.countsTab.title=Counts", + "DataSourceSummaryDialog.detailsTab.title=Details", + "DataSourceSummaryDialog.ingestHistoryTab.title=Ingest History" + }) + DataSourceSummaryDialog(Frame owner) { + super(owner, Bundle.DataSourceSummaryDialog_window_title(), true); + Map usageMap = DataSourceInfoUtilities.getDataSourceTypes(); + Map fileCountsMap = DataSourceInfoUtilities.getCountsOfFiles(); + countsPanel = new DataSourceSummaryCountsPanel(fileCountsMap); + detailsPanel = new DataSourceSummaryDetailsPanel(usageMap); + dataSourcesPanel = new DataSourceBrowser(usageMap, fileCountsMap); + ingestHistoryPanel = new IngestJobInfoPanel(); + initComponents(); + dataSourceSummarySplitPane.setLeftComponent(dataSourcesPanel); + dataSourceTabbedPane.addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); + dataSourceTabbedPane.addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); + dataSourceTabbedPane.addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); + dataSourcesPanel.addListSelectionListener((ListSelectionEvent e) -> { + if (!e.getValueIsAdjusting()) { + DataSource selectedDataSource = dataSourcesPanel.getSelectedDataSource(); + countsPanel.updateCountsTableData(selectedDataSource); + detailsPanel.updateDetailsPanelData(selectedDataSource); + ingestHistoryPanel.updateIngestHistoryData(selectedDataSource); + this.repaint(); + } + }); + this.pack(); + } + + /** + * Make this dialog an observer of the DataSourcesPanel. + */ + void enableObserver() { + dataSourcesPanel.addObserver(this); + } + + @Override + public void update(Observable o, Object arg) { + this.dispose(); + } + + /** + * 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() { + + closeButton = new javax.swing.JButton(); + dataSourceSummarySplitPane = new javax.swing.JSplitPane(); + dataSourceTabbedPane = new javax.swing.JTabbedPane(); + + org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(DataSourceSummaryDialog.class, "DataSourceSummaryDialog.closeButton.text")); // NOI18N + closeButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + closeButtonActionPerformed(evt); + } + }); + + dataSourceSummarySplitPane.setDividerLocation(130); + dataSourceSummarySplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); + dataSourceSummarySplitPane.setRightComponent(dataSourceTabbedPane); + + dataSourceSummarySplitPane.setLeftComponent(dataSourcesPanel); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(dataSourceSummarySplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 668, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(closeButton))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(dataSourceSummarySplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 362, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(closeButton) + .addContainerGap()) + ); + }// //GEN-END:initComponents + + private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed + this.dispose(); + }//GEN-LAST:event_closeButtonActionPerformed + + /** + * Select the data source with the specicied data source id. If no data + * source matches the dataSourceID it will select the first datasource. + * + * @param dataSourceID the ID of the datasource to select, null will cause + * the first datasource to be selected + */ + void selectDataSource(Long dataSourceId) { + dataSourcesPanel.selectDataSource(dataSourceId); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton closeButton; + private javax.swing.JSplitPane dataSourceSummarySplitPane; + private javax.swing.JTabbedPane dataSourceTabbedPane; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java new file mode 100644 index 0000000000..d0038e86fb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java @@ -0,0 +1,212 @@ +/* + * 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.casemodule.datasourcesummary; + +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.Observable; +import java.util.Observer; +import javax.swing.Action; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.nodes.Sheet; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.datamodel.NodeProperty; +import org.sleuthkit.autopsy.directorytree.ViewContextAction; +import org.sleuthkit.datamodel.DataSource; + +/** + * Root node for displaying DataSourceSummary information in the + * DataSourceBrowser + * + */ +final class DataSourceSummaryNode extends AbstractNode { + + private final static Observable closeDialogObservable = new Observable() { + @Override + public void notifyObservers() { + //set changed before notify observers + //we want this observerable to always cause the observer to update when notified + this.setChanged(); + super.notifyObservers(); + } + }; + + /** + * Construct a new DataSourceSummaryNode + * + * @param dataSourceList the list of DataSourceSummary objects representing + * DataSources which are this nodes children + */ + DataSourceSummaryNode(List dataSourceList) { + super(Children.create(new DataSourceSummaryChildren(dataSourceList), true)); + } + + /** + * Add an observer to the closeDialogObservable + * + * @param observer the observer to add + */ + void addObserver(Observer observer) { + closeDialogObservable.addObserver(observer); + } + + /** + * ChildFactory to create children of the DataSourceSummaryNode for each + * DataSourceSummary + */ + static final class DataSourceSummaryChildren extends ChildFactory { + + private final List dataSourceList; + + /** + * Construct a new DataSourceSummaryChildren object to create children + * for each DataSourceSummary object in the list + * + * @param dataSourceList the DataSourceSummary objects to create + * children for + */ + DataSourceSummaryChildren(List dataSourceList) { + this.dataSourceList = dataSourceList; + } + + @Override + protected boolean createKeys(List list) { + if (dataSourceList != null && !dataSourceList.isEmpty()) { + list.addAll(dataSourceList); + } + return true; + } + + @Override + protected Node createNodeForKey(DataSourceSummary key) { + return new DataSourceSummaryEntryNode(key); + } + } + + /** + * A node which represents a single DataSource. + */ + static final class DataSourceSummaryEntryNode extends AbstractNode { + + private final DataSource dataSource; + private final String type; + private final long filesCount; + private final long resultsCount; + private final long tagsCount; + + /** + * Constrcut a new DataSourceSummaryEntryNode + * + * @param dataSourceSummary the DataSourceSummary which is wrapping the + * DataSource which will be represented by this + * node. + */ + DataSourceSummaryEntryNode(DataSourceSummary dataSourceSummary) { + super(Children.LEAF); + dataSource = dataSourceSummary.getDataSource(); + type = dataSourceSummary.getType(); + filesCount = dataSourceSummary.getFilesCount(); + resultsCount = dataSourceSummary.getResultsCount(); + tagsCount = dataSourceSummary.getTagsCount(); + super.setName(dataSource.getName()); + setName(dataSource.getName()); + setDisplayName(dataSource.getName()); + } + + /** + * Get the DataSource being represented by this node. + * + * @return the DataSource which is represented by this node. + */ + DataSource getDataSource() { + return dataSource; + } + + @Messages({"DataSourceSummaryNode.column.dataSourceName.header=Data Source Name", + "DataSourceSummaryNode.column.type.header=Type", + "DataSourceSummaryNode.column.files.header=Files", + "DataSourceSummaryNode.column.results.header=Results", + "DataSourceSummaryNode.column.tags.header=Tags"}) + @Override + protected Sheet createSheet() { + Sheet sheet = super.createSheet(); + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + if (sheetSet == null) { + sheetSet = Sheet.createPropertiesSet(); + sheet.put(sheetSet); + } + sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_dataSourceName_header(), Bundle.DataSourceSummaryNode_column_dataSourceName_header(), Bundle.DataSourceSummaryNode_column_dataSourceName_header(), + dataSource)); + sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_type_header(), Bundle.DataSourceSummaryNode_column_type_header(), Bundle.DataSourceSummaryNode_column_type_header(), + type)); + sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_files_header(), Bundle.DataSourceSummaryNode_column_files_header(), Bundle.DataSourceSummaryNode_column_files_header(), + filesCount)); + sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_results_header(), Bundle.DataSourceSummaryNode_column_results_header(), Bundle.DataSourceSummaryNode_column_results_header(), + resultsCount)); + sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_tags_header(), Bundle.DataSourceSummaryNode_column_tags_header(), Bundle.DataSourceSummaryNode_column_tags_header(), + tagsCount)); + return sheet; + } + + /** + * Returns action to open the Case represented by this node + * + * @return an action which will open the current case + */ + @Override + public Action getPreferredAction() { + return new ViewDataSourceInContextAction(); + } + + @Override + public Action[] getActions(boolean context) { + List actions = new ArrayList<>(); + actions.add(new ViewDataSourceInContextAction()); + return actions.toArray(new Action[actions.size()]); + } + + /** + * Action to navigate to this node's Data Source in the Results Viewer + * and close the DataSourceSummaryDialog. + */ + private class ViewDataSourceInContextAction extends ViewContextAction { + + private static final long serialVersionUID = 1L; + + /** + * Construct a new ViewDataSourceInCOntextAction + */ + @Messages({"DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source"}) + ViewDataSourceInContextAction() { + super(Bundle.DataSourceSummaryNode_viewDataSourceAction_text(), dataSource); + } + + @Override + public void actionPerformed(ActionEvent e) { + closeDialogObservable.notifyObservers(); + super.actionPerformed(e); + } + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/RightAlignedTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/RightAlignedTableCellRenderer.java new file mode 100644 index 0000000000..12f111bba2 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/RightAlignedTableCellRenderer.java @@ -0,0 +1,61 @@ +/* + * 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.casemodule.datasourcesummary; + +import java.awt.Component; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JTable; +import org.openide.nodes.Node; +import org.sleuthkit.autopsy.datamodel.NodeProperty; +import org.sleuthkit.autopsy.guiutils.GrayableCellRenderer; + +/** + * Cell Renderer which extends grayable cell renderer to inherit highlighting + * but right aligns the contents will display the value of the cell or if the + * cell contains a NodeProperty the value of that NodeProperty sets text to + * empty string if null. + */ +final class RightAlignedTableCellRenderer extends GrayableCellRenderer { + + private static final long serialVersionUID = 1L; + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + setHorizontalAlignment(RIGHT); + Object cellContents = null; + if ((value instanceof NodeProperty)) { + //The Outline view has properties in the cell, the value contained in the property is what we want + try { + cellContents = ((Node.Property) value).getValue(); + } catch (IllegalAccessException | InvocationTargetException ex) { + //Unable to get the value from the NodeProperty cell will appear empty + } + } else { + //JTables contain the value we want directly in the cell + cellContents = value; + } + if (null != cellContents) { + setText(cellContents.toString()); + } else { + setText(""); + } + grayCellIfTableNotEnabled(table, isSelected); + return this; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/ViewSummaryInformationAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/ViewSummaryInformationAction.java similarity index 67% rename from Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/ViewSummaryInformationAction.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/ViewSummaryInformationAction.java index 96572fbf51..babe2197a1 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/ViewSummaryInformationAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/ViewSummaryInformationAction.java @@ -16,23 +16,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.casemodule.datasourceSummary; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.awt.Frame; import java.awt.event.ActionEvent; import javax.swing.AbstractAction; -import javax.swing.JDialog; import javax.swing.SwingUtilities; import org.openide.util.NbBundle.Messages; import org.openide.windows.WindowManager; /** - * ViewSummaryInformationAction action for opening a Data Source Summary Panel + * ViewSummaryInformationAction action for opening a Data Sources Summary Dialog * with the specified data source selected if it is present. */ public final class ViewSummaryInformationAction extends AbstractAction { - private static JDialog dataSourceSummaryDialog; + private static DataSourceSummaryDialog dataSourceSummaryDialog; private static Long selectDataSource; private static final long serialVersionUID = 1L; @@ -40,8 +39,8 @@ public final class ViewSummaryInformationAction extends AbstractAction { * Create a ViewSummaryInformationAction for the selected datasource. * * @param selectedDataSource - the data source which is currently selected - * and will be selected initially when the - * DataSourceSummaryPanel opens. + and will be selected initially when the + DataSourceSummaryDialog opens. */ @Messages({"ViewSummaryInformationAction.name.text=View Summary Information"}) public ViewSummaryInformationAction(Long selectedDataSource) { @@ -49,23 +48,16 @@ public final class ViewSummaryInformationAction extends AbstractAction { selectDataSource = selectedDataSource; } - @Messages({"ViewSummaryInformationAction.window.title=Data Source Summary"}) @Override public void actionPerformed(ActionEvent actionEvent) { SwingUtilities.invokeLater(() -> { - String title = Bundle.ViewSummaryInformationAction_window_title(); Frame mainWindow = WindowManager.getDefault().getMainWindow(); - dataSourceSummaryDialog = new JDialog(mainWindow, title, true); - DataSourceSummaryPanel dataSourceSummaryPanel = new DataSourceSummaryPanel(); - //allow the buttons in DataSourceSummaryPanel to close this dialog - dataSourceSummaryPanel.addCloseButtonAction((ActionEvent event) -> { - dataSourceSummaryDialog.dispose(); - }); + dataSourceSummaryDialog = new DataSourceSummaryDialog(mainWindow); + //allow dialog to be closed when actions performed + dataSourceSummaryDialog.enableObserver(); //select the specifed data source - dataSourceSummaryPanel.selectDataSource(selectDataSource); - dataSourceSummaryDialog.add(dataSourceSummaryPanel); + dataSourceSummaryDialog.selectDataSource(selectDataSource); dataSourceSummaryDialog.setResizable(true); - dataSourceSummaryDialog.pack(); dataSourceSummaryDialog.setLocationRelativeTo(mainWindow); dataSourceSummaryDialog.setVisible(true); dataSourceSummaryDialog.toFront(); diff --git a/Core/src/org/sleuthkit/autopsy/core/layer.xml b/Core/src/org/sleuthkit/autopsy/core/layer.xml index 30819d6260..c25f37199e 100644 --- a/Core/src/org/sleuthkit/autopsy/core/layer.xml +++ b/Core/src/org/sleuthkit/autopsy/core/layer.xml @@ -47,8 +47,8 @@ - - + + @@ -180,11 +180,11 @@ - + - - + + diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 9025b325e3..06560e6b13 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -34,7 +34,7 @@ import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.casemodule.datasourceSummary.ViewSummaryInformationAction; +import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor; import org.sleuthkit.autopsy.directorytree.FileSearchAction; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java index 9509ccb483..47f616de12 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java @@ -23,7 +23,7 @@ import java.util.Collections; import java.util.List; import javax.swing.Action; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.datasourceSummary.ViewSummaryInformationAction; +import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.FileSearchAction; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/utils/FileTypeUtils.java b/Core/src/org/sleuthkit/autopsy/datamodel/utils/FileTypeUtils.java index 74545fcc40..b4887cf259 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/utils/FileTypeUtils.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/utils/FileTypeUtils.java @@ -162,7 +162,7 @@ public final class FileTypeUtils { /** * Images, Videos, flash Animations, etc */ - VISUAL(Bundle.FileTypeCategory_Media_displayName(), + VISUAL(Bundle.FileTypeCategory_Visual_displayName(), VISUAL_MEDIA_MIME_TYPES, Collections.emptyList()), /**