mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-14 17:06:16 +00:00
Merge pull request #6205 from gdicristofaro/6709-userActivityConcept
6709 user activity concept
This commit is contained in:
commit
41dad6aef3
@ -27,6 +27,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
import org.sleuthkit.datamodel.DataSource;
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
import org.sleuthkit.datamodel.TskData.TSK_FS_META_FLAG_ENUM;
|
import org.sleuthkit.datamodel.TskData.TSK_FS_META_FLAG_ENUM;
|
||||||
@ -108,14 +109,30 @@ final class DataSourceInfoUtilities {
|
|||||||
* obtained.
|
* obtained.
|
||||||
*/
|
*/
|
||||||
static <T> T getBaseQueryResult(String query, ResultSetHandler<T> processor, String errorMessage) {
|
static <T> T getBaseQueryResult(String query, ResultSetHandler<T> processor, String errorMessage) {
|
||||||
try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query)) {
|
return getBaseQueryResult(SleuthkitCaseProvider.DEFAULT, query, processor, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a result based on the provided query.
|
||||||
|
*
|
||||||
|
* @param provider The means of obtaining a SleuthkitCase.
|
||||||
|
* @param query The query.
|
||||||
|
* @param processor The result set handler.
|
||||||
|
* @param errorMessage The error message to display if there is an error
|
||||||
|
* retrieving the resultset.
|
||||||
|
*
|
||||||
|
* @return The ResultSetHandler value or null if no ResultSet could be
|
||||||
|
* obtained.
|
||||||
|
*/
|
||||||
|
static <T> T getBaseQueryResult(SleuthkitCaseProvider provider, String query, ResultSetHandler<T> processor, String errorMessage) {
|
||||||
|
try (SleuthkitCase.CaseDbQuery dbQuery = provider.get().executeQuery(query)) {
|
||||||
ResultSet resultSet = dbQuery.getResultSet();
|
ResultSet resultSet = dbQuery.getResultSet();
|
||||||
try {
|
try {
|
||||||
return processor.process(resultSet);
|
return processor.process(resultSet);
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
logger.log(Level.WARNING, errorMessage, ex);
|
logger.log(Level.WARNING, errorMessage, ex);
|
||||||
}
|
}
|
||||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
} catch (TskCoreException | SleuthkitCaseProviderException ex) {
|
||||||
logger.log(Level.WARNING, errorMessage, ex);
|
logger.log(Level.WARNING, errorMessage, ex);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides summary information about top domains in a datasource. At this time,
|
||||||
|
* the data being provided is fictitious and is done as a placeholder.
|
||||||
|
*/
|
||||||
|
public class DataSourceTopDomainsSummary {
|
||||||
|
|
||||||
|
private static final long SLEEP_TIME = 5000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function to calculate a result from 2 parameters.
|
||||||
|
*/
|
||||||
|
interface Function2<A1, A2, O> {
|
||||||
|
|
||||||
|
O apply(A1 a1, A2 a2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of recent domains based on the datasource.
|
||||||
|
*
|
||||||
|
* @param dataSource The datasource to query for recent domains.
|
||||||
|
* @param count The max count of items to return.
|
||||||
|
*
|
||||||
|
* @return The list of items retrieved from the database.
|
||||||
|
*
|
||||||
|
* @throws InterruptedException
|
||||||
|
*/
|
||||||
|
public List<TopDomainsResult> getRecentDomains(DataSource dataSource, int count) throws InterruptedException {
|
||||||
|
Thread.sleep(SLEEP_TIME);
|
||||||
|
final String dId = Long.toString(dataSource.getId());
|
||||||
|
final Function2<String, Integer, String> getId = (s, idx) -> String.format("d:%s, f:%s, i:%d", dId, s, idx);
|
||||||
|
return IntStream.range(0, count)
|
||||||
|
.mapToObj(num -> new TopDomainsResult(
|
||||||
|
getId.apply("domain", num),
|
||||||
|
getId.apply("url", num),
|
||||||
|
(long) num,
|
||||||
|
new Date(((long) num) * 1000 * 60 * 60 * 24)
|
||||||
|
))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
@ -66,6 +66,36 @@ public class DataSourceTopProgramsSummary {
|
|||||||
*/
|
*/
|
||||||
private static final String QUERY_SUFFIX = "_query";
|
private static final String QUERY_SUFFIX = "_query";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functions that determine the folder name of a list of path elements. If
|
||||||
|
* not matched, function returns null.
|
||||||
|
*/
|
||||||
|
private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
|
||||||
|
// handle Program Files and Program Files (x86) - if true, return the next folder
|
||||||
|
(pathList) -> {
|
||||||
|
if (pathList.size() < 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String rootParent = pathList.get(0).toUpperCase();
|
||||||
|
if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
|
||||||
|
return pathList.get(1);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// if there is a folder named "APPLICATION DATA" or "APPDATA"
|
||||||
|
(pathList) -> {
|
||||||
|
for (String pathEl : pathList) {
|
||||||
|
String uppered = pathEl.toUpperCase();
|
||||||
|
if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
|
||||||
|
return "AppData";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a sql statement querying the blackboard attributes table for a
|
* Creates a sql statement querying the blackboard attributes table for a
|
||||||
* particular attribute type and returning a specified value. That query
|
* particular attribute type and returning a specified value. That query
|
||||||
@ -138,6 +168,17 @@ public class DataSourceTopProgramsSummary {
|
|||||||
private static String getLikeClause(String column, String likeString, boolean isLike) {
|
private static String getLikeClause(String column, String likeString, boolean isLike) {
|
||||||
return column + (isLike ? "" : " NOT") + " LIKE '" + likeString + "'";
|
return column + (isLike ? "" : " NOT") + " LIKE '" + likeString + "'";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private final SleuthkitCaseProvider provider;
|
||||||
|
|
||||||
|
public DataSourceTopProgramsSummary() {
|
||||||
|
this(SleuthkitCaseProvider.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataSourceTopProgramsSummary(SleuthkitCaseProvider provider) {
|
||||||
|
this.provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a list of the top programs used on the data source. Currently
|
* Retrieves a list of the top programs used on the data source. Currently
|
||||||
@ -149,7 +190,7 @@ public class DataSourceTopProgramsSummary {
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count) {
|
public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count) {
|
||||||
if (dataSource == null || count <= 0) {
|
if (dataSource == null || count <= 0) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
@ -225,38 +266,9 @@ public class DataSourceTopProgramsSummary {
|
|||||||
return progResults;
|
return progResults;
|
||||||
};
|
};
|
||||||
|
|
||||||
return getBaseQueryResult(query, handler, errorMessage);
|
return getBaseQueryResult(provider, query, handler, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Functions that determine the folder name of a list of path elements. If
|
|
||||||
* not matched, function returns null.
|
|
||||||
*/
|
|
||||||
private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
|
|
||||||
// handle Program Files and Program Files (x86) - if true, return the next folder
|
|
||||||
(pathList) -> {
|
|
||||||
if (pathList.size() < 2) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String rootParent = pathList.get(0).toUpperCase();
|
|
||||||
if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
|
|
||||||
return pathList.get(1);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// if there is a folder named "APPLICATION DATA" or "APPDATA"
|
|
||||||
(pathList) -> {
|
|
||||||
for (String pathEl : pathList) {
|
|
||||||
String uppered = pathEl.toUpperCase();
|
|
||||||
if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
|
|
||||||
return "AppData";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines a short folder name if any. Otherwise, returns empty string.
|
* Determines a short folder name if any. Otherwise, returns empty string.
|
||||||
@ -265,7 +277,7 @@ public class DataSourceTopProgramsSummary {
|
|||||||
*
|
*
|
||||||
* @return The short folder name or empty string if not found.
|
* @return The short folder name or empty string if not found.
|
||||||
*/
|
*/
|
||||||
public static String getShortFolderName(String strPath, String applicationName) {
|
public String getShortFolderName(String strPath, String applicationName) {
|
||||||
if (strPath == null) {
|
if (strPath == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -289,7 +301,4 @@ public class DataSourceTopProgramsSummary {
|
|||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataSourceTopProgramsSummary() {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to provide the current SleuthkitCase object. This is to allow
|
||||||
|
* for SleuthkitCase objects to be created and injected in a testing scenario
|
||||||
|
* outside of the context of Case.
|
||||||
|
*
|
||||||
|
* By default, this uses Case.getCurrentCaseThrows().getSleuthkitCase().
|
||||||
|
*/
|
||||||
|
public interface SleuthkitCaseProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown in the event that the SleuthkitCase object cannot be
|
||||||
|
* provided.
|
||||||
|
*/
|
||||||
|
class SleuthkitCaseProviderException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param string The message for the exception.
|
||||||
|
*/
|
||||||
|
public SleuthkitCaseProviderException(String string) {
|
||||||
|
super(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param string The message for the exception.
|
||||||
|
* @param thrwbl The inner exception.
|
||||||
|
*/
|
||||||
|
public SleuthkitCaseProviderException(String string, Throwable thrwbl) {
|
||||||
|
super(string, thrwbl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default SleuthkitCaseProvider. This uses
|
||||||
|
* Case.getCurrentCaseThrows().getSleuthkitCase().
|
||||||
|
*/
|
||||||
|
SleuthkitCaseProvider DEFAULT = () -> {
|
||||||
|
try {
|
||||||
|
return Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||||
|
} catch (NoCurrentCaseException e) {
|
||||||
|
throw new SleuthkitCaseProviderException("No currently open case.", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the current SleuthkitCase object.
|
||||||
|
*
|
||||||
|
* @throws SleuthkitCaseProviderException Thrown if there is an error
|
||||||
|
* providing the case.
|
||||||
|
*/
|
||||||
|
SleuthkitCase get() throws SleuthkitCaseProviderException;
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a result of a program run on a datasource.
|
||||||
|
*/
|
||||||
|
public class TopDomainsResult {
|
||||||
|
|
||||||
|
private final String domain;
|
||||||
|
private final String url;
|
||||||
|
private final Long visitTimes;
|
||||||
|
private final Date lastVisit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a top domain result.
|
||||||
|
*
|
||||||
|
* @param domain The domain.
|
||||||
|
* @param url The url.
|
||||||
|
* @param visitTimes The number of times it was visited.
|
||||||
|
* @param lastVisit The date of the last visit.
|
||||||
|
*/
|
||||||
|
public TopDomainsResult(String domain, String url, Long visitTimes, Date lastVisit) {
|
||||||
|
this.domain = domain;
|
||||||
|
this.url = url;
|
||||||
|
this.visitTimes = visitTimes;
|
||||||
|
this.lastVisit = lastVisit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The domain for the result.
|
||||||
|
*/
|
||||||
|
public String getDomain() {
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The url for the result.
|
||||||
|
*/
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The number of times this site is visited.
|
||||||
|
*/
|
||||||
|
public Long getVisitTimes() {
|
||||||
|
return visitTimes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The date of the last visit.
|
||||||
|
*/
|
||||||
|
public Date getLastVisit() {
|
||||||
|
return lastVisit;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.SwingWorkerSequentialExecutor;
|
||||||
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class from which other tabs in data source summary derive.
|
||||||
|
*/
|
||||||
|
abstract class BaseDataSourceSummaryPanel extends JPanel {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final SwingWorkerSequentialExecutor executor = new SwingWorkerSequentialExecutor();
|
||||||
|
private DataSource dataSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets datasource to visualize in the panel.
|
||||||
|
*
|
||||||
|
* @param dataSource The datasource to use in this panel.
|
||||||
|
*/
|
||||||
|
synchronized void setDataSource(DataSource dataSource) {
|
||||||
|
DataSource oldDataSource = this.dataSource;
|
||||||
|
this.dataSource = dataSource;
|
||||||
|
if (this.dataSource != oldDataSource) {
|
||||||
|
this.executor.cancelRunning();
|
||||||
|
onNewDataSource(this.dataSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits the following swing workers for execution in sequential order. If
|
||||||
|
* there are any previous workers, those workers are cancelled.
|
||||||
|
*
|
||||||
|
* @param workers The workers to submit for execution.
|
||||||
|
*/
|
||||||
|
protected void submit(List<? extends SwingWorker<?, ?>> workers) {
|
||||||
|
executor.submit(workers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a new dataSource is added, this method is called.
|
||||||
|
*
|
||||||
|
* @param dataSource The new dataSource.
|
||||||
|
*/
|
||||||
|
protected abstract void onNewDataSource(DataSource dataSource);
|
||||||
|
}
|
@ -35,4 +35,5 @@ DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text=Unallocated Space:
|
|||||||
DataSourceSummaryDetailsPanel.unallocatedSizeValue.text=
|
DataSourceSummaryDetailsPanel.unallocatedSizeValue.text=
|
||||||
DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category
|
DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category
|
||||||
DataSourceSummaryCountsPanel.resultsByTypeLabel.text=Results by Type
|
DataSourceSummaryCountsPanel.resultsByTypeLabel.text=Results by Type
|
||||||
DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run
|
DataSourceSummaryUserActivityPanel.programsRunLabel.text=Recent Programs
|
||||||
|
DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains
|
||||||
|
@ -73,8 +73,13 @@ DataSourceSummaryTabbedPane_countsTab_title=Counts
|
|||||||
DataSourceSummaryTabbedPane_detailsTab_title=Details
|
DataSourceSummaryTabbedPane_detailsTab_title=Details
|
||||||
DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History
|
DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History
|
||||||
DataSourceSummaryTabbedPane_userActivityTab_title=User Activity
|
DataSourceSummaryTabbedPane_userActivityTab_title=User Activity
|
||||||
DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run
|
DataSourceSummaryUserActivityPanel.programsRunLabel.text=Recent Programs
|
||||||
|
DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains
|
||||||
|
DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists
|
||||||
DataSourceSummaryUserActivityPanel_tab_title=User Activity
|
DataSourceSummaryUserActivityPanel_tab_title=User Activity
|
||||||
|
DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header=Domain
|
||||||
|
DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Access
|
||||||
|
DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header=URL
|
||||||
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times
|
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times
|
||||||
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder
|
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder
|
||||||
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run
|
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.RightAlignedTableCellRenderer;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.beans.PropertyVetoException;
|
import java.beans.PropertyVetoException;
|
||||||
import javax.swing.ListSelectionModel;
|
import javax.swing.ListSelectionModel;
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.NonEditableTableModel;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.table.DefaultTableCellRenderer;
|
import javax.swing.table.DefaultTableCellRenderer;
|
||||||
@ -37,7 +38,7 @@ import org.sleuthkit.datamodel.DataSource;
|
|||||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type",
|
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type",
|
||||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count"
|
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count"
|
||||||
})
|
})
|
||||||
final class DataSourceSummaryCountsPanel extends javax.swing.JPanel {
|
class DataSourceSummaryCountsPanel extends BaseDataSourceSummaryPanel {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@ -60,8 +61,6 @@ final class DataSourceSummaryCountsPanel extends javax.swing.JPanel {
|
|||||||
|
|
||||||
private final FileTypePieChart fileTypePieChart = new FileTypePieChart();
|
private final FileTypePieChart fileTypePieChart = new FileTypePieChart();
|
||||||
|
|
||||||
private DataSource dataSource;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new form DataSourceSummaryCountsPanel
|
* Creates new form DataSourceSummaryCountsPanel
|
||||||
*/
|
*/
|
||||||
@ -73,22 +72,8 @@ final class DataSourceSummaryCountsPanel extends javax.swing.JPanel {
|
|||||||
setDataSource(null);
|
setDataSource(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* The datasource currently used as the model in this panel.
|
protected void onNewDataSource(DataSource dataSource) {
|
||||||
*
|
|
||||||
* @return The datasource currently being used as the model in this panel.
|
|
||||||
*/
|
|
||||||
public DataSource getDataSource() {
|
|
||||||
return dataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets datasource to visualize in the panel.
|
|
||||||
*
|
|
||||||
* @param dataSource The datasource to use in this panel.
|
|
||||||
*/
|
|
||||||
public void setDataSource(DataSource dataSource) {
|
|
||||||
this.dataSource = dataSource;
|
|
||||||
if (dataSource == null || !Case.isCaseOpen()) {
|
if (dataSource == null || !Case.isCaseOpen()) {
|
||||||
updateCountsTableData(EMPTY_PAIRS, EMPTY_PAIRS);
|
updateCountsTableData(EMPTY_PAIRS, EMPTY_PAIRS);
|
||||||
} else {
|
} else {
|
||||||
|
@ -33,7 +33,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
/**
|
/**
|
||||||
* Panel to display additional details associated with a specific DataSource
|
* Panel to display additional details associated with a specific DataSource
|
||||||
*/
|
*/
|
||||||
class DataSourceSummaryDetailsPanel extends javax.swing.JPanel {
|
class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
|
||||||
|
|
||||||
//Because this panel was made using the gridbaglayout and netbean's Customize Layout tool it will be best to continue to modify it through that
|
//Because this panel was made using the gridbaglayout and netbean's Customize Layout tool it will be best to continue to modify it through that
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
@ -41,8 +41,6 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel {
|
|||||||
private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##");
|
private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##");
|
||||||
private static final Logger logger = Logger.getLogger(DataSourceSummaryDetailsPanel.class.getName());
|
private static final Logger logger = Logger.getLogger(DataSourceSummaryDetailsPanel.class.getName());
|
||||||
|
|
||||||
private DataSource dataSource;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new form DataSourceSummaryDetailsPanel
|
* Creates new form DataSourceSummaryDetailsPanel
|
||||||
*/
|
*/
|
||||||
@ -53,23 +51,8 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel {
|
|||||||
setDataSource(null);
|
setDataSource(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* The datasource currently used as the model in this panel.
|
protected void onNewDataSource(DataSource dataSource) {
|
||||||
*
|
|
||||||
* @return The datasource currently being used as the model in this panel.
|
|
||||||
*/
|
|
||||||
public DataSource getDataSource() {
|
|
||||||
return dataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets datasource to visualize in the panel.
|
|
||||||
*
|
|
||||||
* @param dataSource The datasource to use in this panel.
|
|
||||||
*/
|
|
||||||
public void setDataSource(DataSource dataSource) {
|
|
||||||
this.dataSource = dataSource;
|
|
||||||
|
|
||||||
if (dataSource == null || !Case.isCaseOpen()) {
|
if (dataSource == null || !Case.isCaseOpen()) {
|
||||||
updateDetailsPanelData(null, null, null, null);
|
updateDetailsPanelData(null, null, null, null);
|
||||||
} else {
|
} else {
|
||||||
|
@ -18,10 +18,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import javax.swing.JTabbedPane;
|
import javax.swing.JTabbedPane;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
|
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.ui.Bundle;
|
|
||||||
import org.sleuthkit.datamodel.DataSource;
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,9 +41,13 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane {
|
|||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final DataSourceSummaryCountsPanel countsPanel = new DataSourceSummaryCountsPanel();
|
// A pair of the tab name and the corresponding BaseDataSourceSummaryTabs to be displayed.
|
||||||
private final DataSourceSummaryDetailsPanel detailsPanel = new DataSourceSummaryDetailsPanel();
|
private final List<Pair<String, BaseDataSourceSummaryPanel>> tabs = Arrays.asList(
|
||||||
private final DataSourceSummaryUserActivityPanel userActivityPanel = new DataSourceSummaryUserActivityPanel();
|
Pair.of(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new DataSourceSummaryDetailsPanel()),
|
||||||
|
Pair.of(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), new DataSourceSummaryCountsPanel()),
|
||||||
|
Pair.of(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new DataSourceSummaryUserActivityPanel())
|
||||||
|
);
|
||||||
|
|
||||||
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();
|
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();
|
||||||
|
|
||||||
private DataSource dataSource = null;
|
private DataSource dataSource = null;
|
||||||
@ -50,10 +56,12 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane {
|
|||||||
* Constructs a tabbed pane showing the summary of a data source.
|
* Constructs a tabbed pane showing the summary of a data source.
|
||||||
*/
|
*/
|
||||||
public DataSourceSummaryTabbedPane() {
|
public DataSourceSummaryTabbedPane() {
|
||||||
|
for (Pair<String, BaseDataSourceSummaryPanel> tab : tabs) {
|
||||||
|
addTab(tab.getKey(), tab.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
addTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), detailsPanel);
|
// IngestJobInfoPanel is not specifically a data source summary panel
|
||||||
addTab(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), countsPanel);
|
// and is called separately for that reason.
|
||||||
addTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), userActivityPanel);
|
|
||||||
addTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel);
|
addTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,9 +82,12 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane {
|
|||||||
public void setDataSource(DataSource dataSource) {
|
public void setDataSource(DataSource dataSource) {
|
||||||
this.dataSource = dataSource;
|
this.dataSource = dataSource;
|
||||||
|
|
||||||
detailsPanel.setDataSource(dataSource);
|
for (Pair<String, BaseDataSourceSummaryPanel> tab : tabs) {
|
||||||
countsPanel.setDataSource(dataSource);
|
tab.getValue().setDataSource(dataSource);
|
||||||
userActivityPanel.setDataSource(dataSource);
|
}
|
||||||
|
|
||||||
|
// IngestJobInfoPanel is not specifically a data source summary panel
|
||||||
|
// and is called separately for that reason.
|
||||||
ingestHistoryPanel.setDataSource(dataSource);
|
ingestHistoryPanel.setDataSource(dataSource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,59 +11,180 @@
|
|||||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||||
|
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-58,0,0,2,-42"/>
|
||||||
</AuxValues>
|
</AuxValues>
|
||||||
|
|
||||||
<Layout>
|
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||||
<DimensionLayout dim="0">
|
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
|
||||||
<Group type="102" alignment="0" attributes="0">
|
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
|
||||||
<Component id="programsRunLabel" min="-2" pref="155" max="-2" attributes="0"/>
|
|
||||||
<Component id="topProgramsScrollPane" min="-2" pref="460" max="-2" attributes="0"/>
|
|
||||||
</Group>
|
|
||||||
<EmptySpace pref="128" max="32767" attributes="0"/>
|
|
||||||
</Group>
|
|
||||||
</Group>
|
|
||||||
</DimensionLayout>
|
|
||||||
<DimensionLayout dim="1">
|
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
|
||||||
<Group type="102" alignment="0" attributes="0">
|
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
|
||||||
<Component id="programsRunLabel" min="-2" max="-2" attributes="0"/>
|
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
|
||||||
<Component id="topProgramsScrollPane" min="-2" max="-2" attributes="0"/>
|
|
||||||
<EmptySpace max="32767" attributes="0"/>
|
|
||||||
</Group>
|
|
||||||
</Group>
|
|
||||||
</DimensionLayout>
|
|
||||||
</Layout>
|
|
||||||
<SubComponents>
|
<SubComponents>
|
||||||
<Component class="javax.swing.JLabel" name="programsRunLabel">
|
<Container class="javax.swing.JScrollPane" name="contentScrollPane">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryUserActivityPanel.programsRunLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<Dimension value="null"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="null"/>
|
||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
<AuxValues>
|
<AuxValues>
|
||||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
</AuxValues>
|
</AuxValues>
|
||||||
</Component>
|
<Constraints>
|
||||||
<Container class="javax.swing.JScrollPane" name="topProgramsScrollPane">
|
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||||
<Properties>
|
<BorderConstraints direction="Center"/>
|
||||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
</Constraint>
|
||||||
<Dimension value="[750, 187]"/>
|
</Constraints>
|
||||||
</Property>
|
|
||||||
</Properties>
|
|
||||||
<AuxValues>
|
|
||||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
|
||||||
</AuxValues>
|
|
||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||||
<SubComponents>
|
<SubComponents>
|
||||||
<Component class="javax.swing.JTable" name="topProgramsTable">
|
<Container class="javax.swing.JPanel" name="contentPanel">
|
||||||
</Component>
|
<Properties>
|
||||||
|
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||||
|
<Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
|
||||||
|
<EmptyBorder bottom="10" left="10" right="10" top="10"/>
|
||||||
|
</Border>
|
||||||
|
</Property>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[720, 450]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[720, 450]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
|
||||||
|
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
|
||||||
|
<Property name="axis" type="int" value="3"/>
|
||||||
|
</Layout>
|
||||||
|
<SubComponents>
|
||||||
|
<Component class="javax.swing.JLabel" name="programsRunLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="horizontalAlignment" type="int" value="2"/>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryUserActivityPanel.programsRunLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="alignmentX" type="float" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||||
|
<Connection code="Component.LEFT_ALIGNMENT" type="code"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.Box$Filler" name="filler1">
|
||||||
|
<Properties>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[0, 2]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[0, 2]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[0, 2]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.RigidArea"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
<Container class="javax.swing.JPanel" name="topProgramsTablePanel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="alignmentX" type="float" value="0.0"/>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[700, 187]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[700, 187]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[700, 187]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="topProgramsTable"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
|
||||||
|
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||||
|
</Container>
|
||||||
|
<Component class="javax.swing.Box$Filler" name="filler3">
|
||||||
|
<Properties>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[0, 20]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[0, 20]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[0, 20]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.RigidArea"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="recentDomainsLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="horizontalAlignment" type="int" value="2"/>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryUserActivityPanel.recentDomainsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.Box$Filler" name="filler2">
|
||||||
|
<Properties>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[0, 2]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[0, 2]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[0, 2]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.RigidArea"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
<Container class="javax.swing.JPanel" name="recentDomainsTablePanel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="alignmentX" type="float" value="0.0"/>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[700, 187]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[700, 187]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[700, 187]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="recentDomainsTable"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
|
||||||
|
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||||
|
</Container>
|
||||||
|
</SubComponents>
|
||||||
|
</Container>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Container>
|
</Container>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
|
@ -21,19 +21,23 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import javax.swing.JLabel;
|
import java.util.stream.Collectors;
|
||||||
import javax.swing.JTable;
|
|
||||||
import javax.swing.table.AbstractTableModel;
|
|
||||||
import javax.swing.table.DefaultTableCellRenderer;
|
|
||||||
import javax.swing.table.TableCellRenderer;
|
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceTopDomainsSummary;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceTopProgramsSummary;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceTopProgramsSummary;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopDomainsResult;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsResult;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsResult;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.HorizontalAlign;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
|
||||||
import org.sleuthkit.datamodel.DataSource;
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,207 +48,134 @@ import org.sleuthkit.datamodel.DataSource;
|
|||||||
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program",
|
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program",
|
||||||
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder",
|
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder",
|
||||||
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times",
|
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times",
|
||||||
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run"
|
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run",
|
||||||
})
|
"DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header=Domain",
|
||||||
public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel {
|
"DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header=URL",
|
||||||
|
"DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Access",
|
||||||
|
"DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists",})
|
||||||
|
public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
|
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
|
||||||
private static final int TOP_PROGS_COUNT = 10;
|
private static final int TOP_PROGS_COUNT = 10;
|
||||||
private static final DefaultTableCellRenderer RIGHT_ALIGNED_RENDERER = new DefaultTableCellRenderer();
|
private static final int TOP_DOMAINS_COUNT = 10;
|
||||||
|
|
||||||
static {
|
private final JTablePanel<TopProgramsResult> topProgramsTable;
|
||||||
RIGHT_ALIGNED_RENDERER.setHorizontalAlignment(JLabel.RIGHT);
|
private final JTablePanel<TopDomainsResult> recentDomainsTable;
|
||||||
}
|
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
|
||||||
|
private final List<JTablePanel<?>> tables;
|
||||||
private DataSource dataSource;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new form DataSourceUserActivityPanel
|
* Creates a new DataSourceUserActivityPanel.
|
||||||
*/
|
*/
|
||||||
public DataSourceSummaryUserActivityPanel() {
|
public DataSourceSummaryUserActivityPanel() {
|
||||||
|
this(new DataSourceTopProgramsSummary(), new DataSourceTopDomainsSummary());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new DataSourceSummaryUserActivityPanel.
|
||||||
|
*
|
||||||
|
* @param topProgramsData Class from which to obtain top programs data.
|
||||||
|
* @param topDomainsData Class from which to obtain recent domains data.
|
||||||
|
*/
|
||||||
|
public DataSourceSummaryUserActivityPanel(DataSourceTopProgramsSummary topProgramsData, DataSourceTopDomainsSummary topDomainsData) {
|
||||||
|
// set up recent programs table
|
||||||
|
this.topProgramsTable = JTablePanel.getJTablePanel(Arrays.asList(new ColumnModel<>(
|
||||||
|
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(),
|
||||||
|
(prog) -> {
|
||||||
|
return new DefaultCellModel(prog.getProgramName())
|
||||||
|
.setTooltip(prog.getProgramPath());
|
||||||
|
},
|
||||||
|
250),
|
||||||
|
new ColumnModel<>(
|
||||||
|
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(),
|
||||||
|
(prog) -> {
|
||||||
|
return new DefaultCellModel(
|
||||||
|
topProgramsData.getShortFolderName(
|
||||||
|
prog.getProgramPath(),
|
||||||
|
prog.getProgramName()));
|
||||||
|
},
|
||||||
|
150),
|
||||||
|
new ColumnModel<>(
|
||||||
|
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header(),
|
||||||
|
(prog) -> {
|
||||||
|
String runTimes = prog.getRunTimes() == null ? "" : Long.toString(prog.getRunTimes());
|
||||||
|
return new DefaultCellModel(runTimes)
|
||||||
|
.setHorizontalAlignment(HorizontalAlign.RIGHT);
|
||||||
|
},
|
||||||
|
80),
|
||||||
|
new ColumnModel<>(
|
||||||
|
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header(),
|
||||||
|
(prog) -> {
|
||||||
|
String date = prog.getLastRun() == null ? "" : DATETIME_FORMAT.format(prog.getLastRun());
|
||||||
|
return new DefaultCellModel(date)
|
||||||
|
.setHorizontalAlignment(HorizontalAlign.RIGHT);
|
||||||
|
},
|
||||||
|
150)
|
||||||
|
));
|
||||||
|
|
||||||
|
// set up recent domains table
|
||||||
|
this.recentDomainsTable = JTablePanel.getJTablePanel(Arrays.asList(new ColumnModel<>(
|
||||||
|
Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header(),
|
||||||
|
(d) -> new DefaultCellModel(d.getDomain()),
|
||||||
|
250),
|
||||||
|
new ColumnModel<>(
|
||||||
|
Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header(),
|
||||||
|
(d) -> new DefaultCellModel(d.getUrl()),
|
||||||
|
250),
|
||||||
|
new ColumnModel<>(
|
||||||
|
Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header(),
|
||||||
|
(prog) -> {
|
||||||
|
String lastVisit = prog.getLastVisit() == null ? "" : DATETIME_FORMAT.format(prog.getLastVisit());
|
||||||
|
return new DefaultCellModel(lastVisit)
|
||||||
|
.setHorizontalAlignment(HorizontalAlign.RIGHT);
|
||||||
|
},
|
||||||
|
150)
|
||||||
|
));
|
||||||
|
|
||||||
|
this.tables = Arrays.asList(
|
||||||
|
topProgramsTable,
|
||||||
|
recentDomainsTable
|
||||||
|
);
|
||||||
|
|
||||||
|
// set up data acquisition methods
|
||||||
|
dataFetchComponents = Arrays.asList(
|
||||||
|
new DataFetchComponents<DataSource, List<TopProgramsResult>>(
|
||||||
|
(dataSource) -> topProgramsData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
|
||||||
|
(result) -> topProgramsTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(),
|
||||||
|
Bundle.DataSourceSummaryUserActivityPanel_noDataExists())),
|
||||||
|
new DataFetchComponents<DataSource, List<TopDomainsResult>>(
|
||||||
|
(dataSource) -> topDomainsData.getRecentDomains(dataSource, TOP_DOMAINS_COUNT),
|
||||||
|
(result) -> recentDomainsTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(),
|
||||||
|
Bundle.DataSourceSummaryUserActivityPanel_noDataExists()))
|
||||||
|
);
|
||||||
|
|
||||||
initComponents();
|
initComponents();
|
||||||
topProgramsTable.getTableHeader().setReorderingAllowed(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* The datasource currently used as the model in this panel.
|
protected void onNewDataSource(DataSource dataSource) {
|
||||||
*
|
// if no data source is present or the case is not open,
|
||||||
* @return The datasource currently being used as the model in this panel.
|
// set results for tables to null.
|
||||||
*/
|
|
||||||
public DataSource getDataSource() {
|
|
||||||
return dataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets datasource to visualize in the panel.
|
|
||||||
*
|
|
||||||
* @param dataSource The datasource to use in this panel.
|
|
||||||
*/
|
|
||||||
public void setDataSource(DataSource dataSource) {
|
|
||||||
this.dataSource = dataSource;
|
|
||||||
if (dataSource == null || !Case.isCaseOpen()) {
|
if (dataSource == null || !Case.isCaseOpen()) {
|
||||||
updateTopPrograms(new TopProgramsModel(null));
|
this.dataFetchComponents.forEach((item) -> item.getResultHandler()
|
||||||
|
.accept(DataFetchResult.getSuccessResult(null)));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
updateTopPrograms(getTopProgramsModel(dataSource));
|
// set tables to display loading screen
|
||||||
|
this.tables.forEach((table) -> table.showDefaultLoadingMessage());
|
||||||
|
|
||||||
|
// create swing workers to run for each table
|
||||||
|
List<DataFetchWorker<?, ?>> workers = dataFetchComponents
|
||||||
|
.stream()
|
||||||
|
.map((components) -> new DataFetchWorker<>(components, dataSource))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// submit swing workers to run
|
||||||
|
submit(workers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the Top Programs Table in the gui.
|
|
||||||
*
|
|
||||||
* @param data The data in Object[][] form to be used by the
|
|
||||||
* DefaultTableModel.
|
|
||||||
*/
|
|
||||||
private void updateTopPrograms(TopProgramsModel model) {
|
|
||||||
topProgramsTable.setModel(model);
|
|
||||||
topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(250);
|
|
||||||
topProgramsTable.getColumnModel().getColumn(0).setCellRenderer(PATH_CELL_RENDERER);
|
|
||||||
topProgramsTable.getColumnModel().getColumn(1).setPreferredWidth(150);
|
|
||||||
topProgramsTable.getColumnModel().getColumn(2).setCellRenderer(RIGHT_ALIGNED_RENDERER);
|
|
||||||
topProgramsTable.getColumnModel().getColumn(2).setPreferredWidth(80);
|
|
||||||
topProgramsTable.getColumnModel().getColumn(3).setPreferredWidth(150);
|
|
||||||
topProgramsScrollPane.getVerticalScrollBar().setValue(0);
|
|
||||||
this.repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The counts of top programs run.
|
|
||||||
*
|
|
||||||
* @param selectedDataSource The DataSource.
|
|
||||||
*
|
|
||||||
* @return The JTable data model of counts of program runs.
|
|
||||||
*/
|
|
||||||
private static TopProgramsModel getTopProgramsModel(DataSource selectedDataSource) {
|
|
||||||
List<TopProgramsResult> topProgramList
|
|
||||||
= DataSourceTopProgramsSummary.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT);
|
|
||||||
|
|
||||||
if (topProgramList == null) {
|
|
||||||
return new TopProgramsModel(null);
|
|
||||||
} else {
|
|
||||||
return new TopProgramsModel(topProgramList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A POJO defining the values present in the name cell. Defines the name as
|
|
||||||
* well as the path for the tooltip.
|
|
||||||
*/
|
|
||||||
private static class ProgramNameCellValue {
|
|
||||||
|
|
||||||
private final String programName;
|
|
||||||
private final String programPath;
|
|
||||||
|
|
||||||
ProgramNameCellValue(String programName, String programPath) {
|
|
||||||
this.programName = programName;
|
|
||||||
this.programPath = programPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
// override so that the value in the cell reads as programName
|
|
||||||
return programName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The program name.
|
|
||||||
*/
|
|
||||||
String getProgramName() {
|
|
||||||
return programName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The path of the program.
|
|
||||||
*/
|
|
||||||
String getProgramPath() {
|
|
||||||
return programPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines a cell renderer for the first cell rendering the name as the text
|
|
||||||
* and path as the tooltip.
|
|
||||||
*/
|
|
||||||
private static TableCellRenderer PATH_CELL_RENDERER = new DefaultTableCellRenderer() {
|
|
||||||
|
|
||||||
public Component getTableCellRendererComponent(
|
|
||||||
JTable table, Object value,
|
|
||||||
boolean isSelected, boolean hasFocus,
|
|
||||||
int row, int column) {
|
|
||||||
JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
|
||||||
if (value instanceof ProgramNameCellValue) {
|
|
||||||
ProgramNameCellValue cellValue = (ProgramNameCellValue) value;
|
|
||||||
c.setToolTipText(cellValue.getProgramPath());
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines the table model for a JTable of the programs. Accepts a list of
|
|
||||||
* TopProgramsResult objects as rows data source.
|
|
||||||
*/
|
|
||||||
private static class TopProgramsModel extends AbstractTableModel {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
// column headers for artifact counts table
|
|
||||||
private static final String[] TOP_PROGS_COLUMN_HEADERS = new String[]{
|
|
||||||
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(),
|
|
||||||
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(),
|
|
||||||
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header(),
|
|
||||||
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header()
|
|
||||||
};
|
|
||||||
|
|
||||||
private final List<TopProgramsResult> programResults;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main constructor.
|
|
||||||
*
|
|
||||||
* @param programResults The results to display.
|
|
||||||
*/
|
|
||||||
TopProgramsModel(List<TopProgramsResult> programResults) {
|
|
||||||
this.programResults = programResults == null ? new ArrayList<>() : Collections.unmodifiableList(programResults);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getColumnName(int column) {
|
|
||||||
return column < 0 || column >= TOP_PROGS_COLUMN_HEADERS.length ? null : TOP_PROGS_COLUMN_HEADERS[column];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getRowCount() {
|
|
||||||
return programResults.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getColumnCount() {
|
|
||||||
return TOP_PROGS_COLUMN_HEADERS.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
|
||||||
if (rowIndex < 0 || rowIndex >= programResults.size()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
TopProgramsResult result = programResults.get(rowIndex);
|
|
||||||
switch (columnIndex) {
|
|
||||||
case 0:
|
|
||||||
return new ProgramNameCellValue(result.getProgramName(), result.getProgramPath());
|
|
||||||
case 1:
|
|
||||||
return DataSourceTopProgramsSummary.getShortFolderName(result.getProgramPath(), result.getProgramName());
|
|
||||||
case 2:
|
|
||||||
return result.getRunTimes();
|
|
||||||
case 3:
|
|
||||||
return result.getLastRun() == null ? null : DATETIME_FORMAT.format(result.getLastRun());
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called from within the constructor to initialize the form.
|
* 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
|
* WARNING: Do NOT modify this code. The content of this method is always
|
||||||
@ -254,40 +185,56 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel {
|
|||||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
|
|
||||||
|
javax.swing.JScrollPane contentScrollPane = new javax.swing.JScrollPane();
|
||||||
|
javax.swing.JPanel contentPanel = new javax.swing.JPanel();
|
||||||
javax.swing.JLabel programsRunLabel = new javax.swing.JLabel();
|
javax.swing.JLabel programsRunLabel = new javax.swing.JLabel();
|
||||||
topProgramsScrollPane = new javax.swing.JScrollPane();
|
javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2));
|
||||||
topProgramsTable = new javax.swing.JTable();
|
javax.swing.JPanel topProgramsTablePanel = topProgramsTable;
|
||||||
|
javax.swing.Box.Filler filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20));
|
||||||
|
javax.swing.JLabel recentDomainsLabel = new javax.swing.JLabel();
|
||||||
|
javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2));
|
||||||
|
javax.swing.JPanel recentDomainsTablePanel = recentDomainsTable;
|
||||||
|
|
||||||
|
setMaximumSize(null);
|
||||||
|
setLayout(new java.awt.BorderLayout());
|
||||||
|
|
||||||
|
contentScrollPane.setMaximumSize(null);
|
||||||
|
contentScrollPane.setMinimumSize(null);
|
||||||
|
|
||||||
|
contentPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
contentPanel.setMaximumSize(new java.awt.Dimension(720, 450));
|
||||||
|
contentPanel.setMinimumSize(new java.awt.Dimension(720, 450));
|
||||||
|
contentPanel.setLayout(new javax.swing.BoxLayout(contentPanel, javax.swing.BoxLayout.PAGE_AXIS));
|
||||||
|
|
||||||
|
programsRunLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N
|
org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N
|
||||||
|
programsRunLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
|
contentPanel.add(programsRunLabel);
|
||||||
|
contentPanel.add(filler1);
|
||||||
|
|
||||||
topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(750, 187));
|
topProgramsTablePanel.setAlignmentX(0.0F);
|
||||||
topProgramsScrollPane.setViewportView(topProgramsTable);
|
topProgramsTablePanel.setMaximumSize(new java.awt.Dimension(700, 187));
|
||||||
|
topProgramsTablePanel.setMinimumSize(new java.awt.Dimension(700, 187));
|
||||||
|
topProgramsTablePanel.setPreferredSize(new java.awt.Dimension(700, 187));
|
||||||
|
contentPanel.add(topProgramsTablePanel);
|
||||||
|
contentPanel.add(filler3);
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
recentDomainsLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
||||||
this.setLayout(layout);
|
org.openide.awt.Mnemonics.setLocalizedText(recentDomainsLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.recentDomainsLabel.text")); // NOI18N
|
||||||
layout.setHorizontalGroup(
|
contentPanel.add(recentDomainsLabel);
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
contentPanel.add(filler2);
|
||||||
.addGroup(layout.createSequentialGroup()
|
|
||||||
.addContainerGap()
|
recentDomainsTablePanel.setAlignmentX(0.0F);
|
||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
recentDomainsTablePanel.setMaximumSize(new java.awt.Dimension(700, 187));
|
||||||
.addComponent(programsRunLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE)
|
recentDomainsTablePanel.setMinimumSize(new java.awt.Dimension(700, 187));
|
||||||
.addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 460, javax.swing.GroupLayout.PREFERRED_SIZE))
|
recentDomainsTablePanel.setPreferredSize(new java.awt.Dimension(700, 187));
|
||||||
.addContainerGap(128, Short.MAX_VALUE))
|
contentPanel.add(recentDomainsTablePanel);
|
||||||
);
|
|
||||||
layout.setVerticalGroup(
|
contentScrollPane.setViewportView(contentPanel);
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addGroup(layout.createSequentialGroup()
|
add(contentScrollPane, java.awt.BorderLayout.CENTER);
|
||||||
.addContainerGap()
|
|
||||||
.addComponent(programsRunLabel)
|
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
|
||||||
.addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
|
||||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
|
||||||
);
|
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JScrollPane topProgramsScrollPane;
|
|
||||||
private javax.swing.JTable topProgramsTable;
|
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
JTablePanel_errorMessage_defaultText=There was an error loading results.
|
||||||
|
JTablePanel_loadingMessage_defaultText=Loading results...
|
||||||
|
JTablePanel_noDataExists_defaultText=No data exists.
|
@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.table.DefaultTableCellRenderer;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Table cell renderer that renders a cell of a table based off of the
|
||||||
|
* CellModel interface provided within this class.
|
||||||
|
*/
|
||||||
|
public class CellModelTableCellRenderer extends DefaultTableCellRenderer {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the horizontal alignment.
|
||||||
|
*/
|
||||||
|
public enum HorizontalAlign {
|
||||||
|
LEFT(JLabel.LEFT),
|
||||||
|
CENTER(JLabel.CENTER),
|
||||||
|
RIGHT(JLabel.RIGHT);
|
||||||
|
|
||||||
|
private final int jlabelAlignment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a HorizontalAlign enum.
|
||||||
|
*
|
||||||
|
* @param jlabelAlignment The corresponding JLabel horizontal alignment
|
||||||
|
* number.
|
||||||
|
*/
|
||||||
|
HorizontalAlign(int jlabelAlignment) {
|
||||||
|
this.jlabelAlignment = jlabelAlignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The corresponding JLabel horizontal alignment (i.e.
|
||||||
|
* JLabel.LEFT).
|
||||||
|
*/
|
||||||
|
int getJLabelAlignment() {
|
||||||
|
return this.jlabelAlignment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic interface for a cell model.
|
||||||
|
*/
|
||||||
|
public interface CellModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The text to be shown in the cell.
|
||||||
|
*/
|
||||||
|
String getText();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The tooltip (if any) to be displayed in the cell.
|
||||||
|
*/
|
||||||
|
String getTooltip();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The horizontal alignment for the text in the cell.
|
||||||
|
*/
|
||||||
|
HorizontalAlign getHorizontalAlignment();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default cell model.
|
||||||
|
*/
|
||||||
|
public static class DefaultCellModel implements CellModel {
|
||||||
|
|
||||||
|
private final String text;
|
||||||
|
private String tooltip;
|
||||||
|
private HorizontalAlign horizontalAlignment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param text The text to be displayed in the cell.
|
||||||
|
*/
|
||||||
|
public DefaultCellModel(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTooltip() {
|
||||||
|
return tooltip;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the tooltip for this cell model.
|
||||||
|
*
|
||||||
|
* @param tooltip The tooltip for the cell model.
|
||||||
|
*
|
||||||
|
* @return As a utility, returns this.
|
||||||
|
*/
|
||||||
|
public DefaultCellModel setTooltip(String tooltip) {
|
||||||
|
this.tooltip = tooltip;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HorizontalAlign getHorizontalAlignment() {
|
||||||
|
return horizontalAlignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the horizontal alignment for this cell model.
|
||||||
|
*
|
||||||
|
* @param alignment The horizontal alignment for the cell model.
|
||||||
|
*
|
||||||
|
* @return As a utility, returns this.
|
||||||
|
*/
|
||||||
|
public DefaultCellModel setHorizontalAlignment(HorizontalAlign alignment) {
|
||||||
|
this.horizontalAlignment = alignment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int DEFAULT_ALIGNMENT = JLabel.LEFT;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTableCellRendererComponent(JTable table, Object value,
|
||||||
|
boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
|
||||||
|
JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
if (value instanceof CellModel) {
|
||||||
|
return getTableCellRendererComponent(c, (CellModel) value);
|
||||||
|
} else {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customizes the jlabel to match the column model and cell model provided.
|
||||||
|
*
|
||||||
|
* @param defaultCell The cell to customize that will be displayed in the
|
||||||
|
* jtable.
|
||||||
|
* @param cellModel The cell model for this cell.
|
||||||
|
*
|
||||||
|
* @return The provided defaultCell.
|
||||||
|
*/
|
||||||
|
protected Component getTableCellRendererComponent(JLabel defaultCell, CellModel cellModel) {
|
||||||
|
// sets the text for the cell or null if not present.
|
||||||
|
String text = cellModel.getText();
|
||||||
|
if (StringUtils.isNotBlank(text)) {
|
||||||
|
defaultCell.setText(text);
|
||||||
|
} else {
|
||||||
|
defaultCell.setText(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets the tooltip for the cell if present.
|
||||||
|
String tooltip = cellModel.getTooltip();
|
||||||
|
if (StringUtils.isNotBlank(tooltip)) {
|
||||||
|
defaultCell.setToolTipText(tooltip);
|
||||||
|
} else {
|
||||||
|
defaultCell.setToolTipText(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets the JLabel alignment (left, center, right) or default alignment
|
||||||
|
// if no alignment is specified
|
||||||
|
int alignment = (cellModel.getHorizontalAlignment() == null)
|
||||||
|
? DEFAULT_ALIGNMENT
|
||||||
|
: cellModel.getHorizontalAlignment().getJLabelAlignment();
|
||||||
|
defaultCell.setHorizontalAlignment(alignment);
|
||||||
|
|
||||||
|
return defaultCell;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of a loading process.
|
||||||
|
*/
|
||||||
|
public final class DataFetchResult<R> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of result.
|
||||||
|
*/
|
||||||
|
public enum ResultType {
|
||||||
|
SUCCESS, ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a DataFetchResult of loaded data including the data.
|
||||||
|
*
|
||||||
|
* @param data The data.
|
||||||
|
*
|
||||||
|
* @return The loaded data result.
|
||||||
|
*/
|
||||||
|
public static <R> DataFetchResult<R> getSuccessResult(R data) {
|
||||||
|
return new DataFetchResult<>(ResultType.SUCCESS, data, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an error result.
|
||||||
|
*
|
||||||
|
* @param e The exception (if any) present with the error.
|
||||||
|
*
|
||||||
|
* @return The error result.
|
||||||
|
*/
|
||||||
|
public static <R> DataFetchResult<R> getErrorResult(Throwable e) {
|
||||||
|
return new DataFetchResult<>(ResultType.ERROR, null, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ResultType state;
|
||||||
|
private final R data;
|
||||||
|
private final Throwable exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor for the DataLoadingResult.
|
||||||
|
*
|
||||||
|
* @param state The state of the result.
|
||||||
|
* @param data If the result is SUCCESS, the data related to this
|
||||||
|
* result.
|
||||||
|
* @param exception If the result is ERROR, the related exception.
|
||||||
|
*/
|
||||||
|
private DataFetchResult(ResultType state, R data, Throwable exception) {
|
||||||
|
this.state = state;
|
||||||
|
this.data = data;
|
||||||
|
this.exception = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The current loading state.
|
||||||
|
*/
|
||||||
|
public ResultType getResultType() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The data if the state is SUCCESS.
|
||||||
|
*/
|
||||||
|
public R getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The exception if the state is ERROR.
|
||||||
|
*/
|
||||||
|
public Throwable getException() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Swing worker that accepts an argument of a data fetcher and a result
|
||||||
|
* handler. If the data fetcher throws an InterruptedException, it is treated as
|
||||||
|
* a cancellation and not passed to the result handler.
|
||||||
|
*/
|
||||||
|
public class DataFetchWorker<A, R> extends SwingWorker<R, Void> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the functions necessary for a DataFetchWorker. Includes the fetcher
|
||||||
|
* and result handler. The args are not included since they are likely
|
||||||
|
* dynamic.
|
||||||
|
*/
|
||||||
|
public static class DataFetchComponents<A1, R1> {
|
||||||
|
|
||||||
|
private final DataFetcher<A1, R1> fetcher;
|
||||||
|
private final Consumer<DataFetchResult<R1>> resultHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param fetcher The fetcher to be used as an argument for the
|
||||||
|
* DataFetchWorker.
|
||||||
|
* @param resultHandler The result handler to be used as an argument for
|
||||||
|
* the DataFetchWorker.
|
||||||
|
*/
|
||||||
|
public DataFetchComponents(DataFetcher<A1, R1> fetcher, Consumer<DataFetchResult<R1>> resultHandler) {
|
||||||
|
this.fetcher = fetcher;
|
||||||
|
this.resultHandler = resultHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The function that fetches the data.
|
||||||
|
*/
|
||||||
|
public DataFetcher<A1, R1> getFetcher() {
|
||||||
|
return fetcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return When those results are received, this function handles
|
||||||
|
* presenting the results in the UI.
|
||||||
|
*/
|
||||||
|
public Consumer<DataFetchResult<R1>> getResultHandler() {
|
||||||
|
return resultHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(DataFetchWorker.class.getName());
|
||||||
|
|
||||||
|
private final A args;
|
||||||
|
private final DataFetcher<A, R> processor;
|
||||||
|
private final Consumer<DataFetchResult<R>> resultHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor for this swing worker.
|
||||||
|
*
|
||||||
|
* @param components Accepts a components arg which provides a data
|
||||||
|
* processor and a results consumer.
|
||||||
|
* @param args The argument to be provided to the data processor.
|
||||||
|
*/
|
||||||
|
public DataFetchWorker(DataFetchComponents<A, R> components, A args) {
|
||||||
|
this(components.getFetcher(), components.getResultHandler(), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor for this swing worker.
|
||||||
|
*
|
||||||
|
* @param processor The function that will do the fetching of the data
|
||||||
|
* provided the given args. InterruptedException's are
|
||||||
|
* treated as cancellations and are not passed to the
|
||||||
|
* result handler.
|
||||||
|
* @param resultHandler The ui function that will handle the result of the
|
||||||
|
* data processing.
|
||||||
|
* @param args The args provided to the data processor.
|
||||||
|
*/
|
||||||
|
public DataFetchWorker(
|
||||||
|
DataFetcher<A, R> processor,
|
||||||
|
Consumer<DataFetchResult<R>> resultHandler,
|
||||||
|
A args) {
|
||||||
|
|
||||||
|
this.args = args;
|
||||||
|
this.processor = processor;
|
||||||
|
this.resultHandler = resultHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected R doInBackground() throws Exception {
|
||||||
|
return processor.runQuery(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void done() {
|
||||||
|
// if cancelled, simply return
|
||||||
|
if (Thread.interrupted() || isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
R result = null;
|
||||||
|
try {
|
||||||
|
result = get();
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
// if cancelled, simply return
|
||||||
|
return;
|
||||||
|
} catch (ExecutionException ex) {
|
||||||
|
Throwable inner = ex.getCause();
|
||||||
|
// if cancelled during operation, simply return
|
||||||
|
if (inner instanceof InterruptedException) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, there is an error to log
|
||||||
|
logger.log(Level.WARNING, "There was an error while fetching results.", ex);
|
||||||
|
|
||||||
|
// and pass the result to the client
|
||||||
|
resultHandler.accept(DataFetchResult.getErrorResult(inner));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if cancelled, simply return
|
||||||
|
if (Thread.interrupted() || isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the data is loaded, send the data to the consumer.
|
||||||
|
resultHandler.accept(DataFetchResult.getSuccessResult(result));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that accepts input of type I and outputs type O. This function is
|
||||||
|
* meant to be utilized with DataFetchWorker and can therefore, throw an
|
||||||
|
* interrupted exception if the processing is cancelled or an Exception of on
|
||||||
|
* another type in the event that the fetching encountered an error.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface DataFetcher<I, O> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that accepts an input argument and outputs a result. Since it
|
||||||
|
* is meant to be used with the DataFetchWorker, it may throw an interrupted
|
||||||
|
* exception if the thread has been interrupted. It throws another type of
|
||||||
|
* exception if there is an error during fetching.
|
||||||
|
*
|
||||||
|
* @param input The input argument.
|
||||||
|
*
|
||||||
|
* @return The output result.
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
O runQuery(I input) throws Exception;
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import javax.swing.table.AbstractTableModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A TableModel for a JTable designed to show a list of data where each item in
|
||||||
|
* the list represents a row.
|
||||||
|
*/
|
||||||
|
public class DefaultListTableModel<T> extends AbstractTableModel implements ListTableModel<T> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final List<Function<T, ? extends Object>> columns;
|
||||||
|
private List<T> dataRows = Collections.emptyList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param columns A list of functions where the index of each function
|
||||||
|
* represents the data to be displayed at each column index.
|
||||||
|
* The data displayed at row 'r' and column 'c' will be the
|
||||||
|
* result of columns.get(c).apply(dataRows.get(r)).
|
||||||
|
*/
|
||||||
|
public DefaultListTableModel(List<Function<T, ? extends Object>> columns) {
|
||||||
|
this.columns = columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<T> getDataRows() {
|
||||||
|
return dataRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDataRows(List<T> dataRows) {
|
||||||
|
this.dataRows = dataRows == null ? Collections.emptyList() : new ArrayList<>(dataRows);
|
||||||
|
super.fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRowCount() {
|
||||||
|
return dataRows.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnCount() {
|
||||||
|
return columns.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||||
|
// if index requested is null, return null
|
||||||
|
if (rowIndex < 0 || rowIndex >= dataRows.size() || columnIndex < 0 || columnIndex >= columns.size()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, get the corresponding row and use the corresponding
|
||||||
|
// column function to get the value
|
||||||
|
return columns.get(columnIndex).apply(dataRows.get(rowIndex));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,419 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JLayer;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.plaf.LayerUI;
|
||||||
|
import javax.swing.table.DefaultTableColumnModel;
|
||||||
|
import javax.swing.table.TableColumn;
|
||||||
|
import javax.swing.table.TableColumnModel;
|
||||||
|
import org.openide.util.NbBundle.Messages;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.CellModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A table that displays a list of items and also can display messages for
|
||||||
|
* loading, load error, and not loaded.
|
||||||
|
*/
|
||||||
|
@Messages({
|
||||||
|
"JTablePanel_loadingMessage_defaultText=Loading results...",
|
||||||
|
"JTablePanel_errorMessage_defaultText=There was an error loading results.",
|
||||||
|
"JTablePanel_noDataExists_defaultText=No data exists.",})
|
||||||
|
public class JTablePanel<T> extends JPanel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JTables don't allow displaying messages. So this LayerUI is used to
|
||||||
|
* display the contents of a child JLabel. Inspired by TableWaitLayerTest
|
||||||
|
* (Animating a Busy Indicator):
|
||||||
|
* https://docs.oracle.com/javase/tutorial/uiswing/misc/jlayer.html.
|
||||||
|
*/
|
||||||
|
private static class Overlay extends LayerUI<JComponent> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final JLabel label;
|
||||||
|
private boolean visible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor for the Overlay.
|
||||||
|
*/
|
||||||
|
Overlay() {
|
||||||
|
label = new JLabel();
|
||||||
|
label.setHorizontalAlignment(JLabel.CENTER);
|
||||||
|
label.setVerticalAlignment(JLabel.CENTER);
|
||||||
|
label.setOpaque(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether or not this message overlay should be visible.
|
||||||
|
*/
|
||||||
|
boolean isVisible() {
|
||||||
|
return visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this layer visible when painted. In order to be shown in UI,
|
||||||
|
* this component needs to be repainted.
|
||||||
|
*
|
||||||
|
* @param visible Whether or not it is visible.
|
||||||
|
*/
|
||||||
|
void setVisible(boolean visible) {
|
||||||
|
this.visible = visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the message to be displayed in the child jlabel.
|
||||||
|
*
|
||||||
|
* @param message The message to be displayed.
|
||||||
|
*/
|
||||||
|
void setMessage(String message) {
|
||||||
|
label.setText(String.format("<html><div style='text-align: center;'>%s</div></html>",
|
||||||
|
message == null ? "" : message));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint(Graphics g, JComponent c) {
|
||||||
|
// Paint the underlying view.
|
||||||
|
super.paint(g, c);
|
||||||
|
|
||||||
|
if (!visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int w = c.getWidth();
|
||||||
|
int h = c.getHeight();
|
||||||
|
|
||||||
|
// paint the jlabel if visible.
|
||||||
|
label.setBounds(0, 0, w, h);
|
||||||
|
label.paint(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes aspects of a column which can be used with getTableModel or
|
||||||
|
* getJTablePanel. 'T' represents the object that will represent rows in the
|
||||||
|
* table.
|
||||||
|
*/
|
||||||
|
public static class ColumnModel<T> {
|
||||||
|
|
||||||
|
private final String headerTitle;
|
||||||
|
private final Function<T, CellModelTableCellRenderer.CellModel> cellRenderer;
|
||||||
|
private final Integer width;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a DataResultColumnModel.
|
||||||
|
*
|
||||||
|
* @param headerTitle The title for the column.
|
||||||
|
* @param cellRenderer The method that generates a CellModel for the
|
||||||
|
* column based on the data.
|
||||||
|
*/
|
||||||
|
public ColumnModel(String headerTitle, Function<T, CellModelTableCellRenderer.CellModel> cellRenderer) {
|
||||||
|
this(headerTitle, cellRenderer, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a DataResultColumnModel.
|
||||||
|
*
|
||||||
|
* @param headerTitle The title for the column.
|
||||||
|
* @param cellRenderer The method that generates a CellModel for the
|
||||||
|
* column based on the data.
|
||||||
|
* @param width The preferred width of the column.
|
||||||
|
*/
|
||||||
|
public ColumnModel(String headerTitle, Function<T, CellModelTableCellRenderer.CellModel> cellRenderer, Integer width) {
|
||||||
|
this.headerTitle = headerTitle;
|
||||||
|
this.cellRenderer = cellRenderer;
|
||||||
|
this.width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The title for the column.
|
||||||
|
*/
|
||||||
|
public String getHeaderTitle() {
|
||||||
|
return headerTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The method that generates a CellModel for the column based on
|
||||||
|
* the data.
|
||||||
|
*/
|
||||||
|
public Function<T, CellModel> getCellRenderer() {
|
||||||
|
return cellRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The preferred width of the column (can be null).
|
||||||
|
*/
|
||||||
|
public Integer getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(JTablePanel.class.getName());
|
||||||
|
|
||||||
|
private static final String DEFAULT_LOADING_MESSAGE = Bundle.JTablePanel_loadingMessage_defaultText();
|
||||||
|
private static final String DEFAULT_ERROR_MESSAGE = Bundle.JTablePanel_errorMessage_defaultText();
|
||||||
|
private static final String DEFAULT_NO_RESULTS_MESSAGE = Bundle.JTablePanel_noDataExists_defaultText();
|
||||||
|
|
||||||
|
private static final CellModelTableCellRenderer DEFAULT_CELL_RENDERER = new CellModelTableCellRenderer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a TableColumnModel based on the column definitions.
|
||||||
|
*
|
||||||
|
* @param columns The column definitions.
|
||||||
|
*
|
||||||
|
* @return The corresponding TableColumnModel to be used with a JTable.
|
||||||
|
*/
|
||||||
|
public static <T> TableColumnModel getTableColumnModel(List<ColumnModel<T>> columns) {
|
||||||
|
TableColumnModel tableModel = new DefaultTableColumnModel();
|
||||||
|
|
||||||
|
for (int i = 0; i < columns.size(); i++) {
|
||||||
|
TableColumn col = new TableColumn(i);
|
||||||
|
ColumnModel<T> model = columns.get(i);
|
||||||
|
// if a preferred width is specified in the column definition,
|
||||||
|
// set the underlying TableColumn preferred width.
|
||||||
|
if (model.getWidth() != null && model.getWidth() >= 0) {
|
||||||
|
col.setPreferredWidth(model.getWidth());
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the title
|
||||||
|
col.setHeaderValue(model.getHeaderTitle());
|
||||||
|
|
||||||
|
// use the cell model renderer in this instance
|
||||||
|
col.setCellRenderer(DEFAULT_CELL_RENDERER);
|
||||||
|
|
||||||
|
tableModel.addColumn(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tableModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a ListTableModel based on the column definitions provided where
|
||||||
|
* 'T' is the object representing each row.
|
||||||
|
*
|
||||||
|
* @param columns The column definitions.
|
||||||
|
*
|
||||||
|
* @return The corresponding ListTableModel.
|
||||||
|
*/
|
||||||
|
public static <T> ListTableModel<T> getTableModel(List<ColumnModel<T>> columns) {
|
||||||
|
List<Function<T, ? extends Object>> columnRenderers = columns.stream()
|
||||||
|
.map((colModel) -> colModel.getCellRenderer())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return new DefaultListTableModel<T>(columnRenderers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a JTablePanel corresponding to the provided column definitions
|
||||||
|
* where 'T' is the object representing each row.
|
||||||
|
*
|
||||||
|
* @param columns The column definitions.
|
||||||
|
*
|
||||||
|
* @return The corresponding JTablePanel.
|
||||||
|
*/
|
||||||
|
public static <T> JTablePanel<T> getJTablePanel(List<ColumnModel<T>> columns) {
|
||||||
|
ListTableModel<T> tableModel = getTableModel(columns);
|
||||||
|
JTablePanel<T> resultTable = new JTablePanel<>(tableModel);
|
||||||
|
return resultTable.setColumnModel(getTableColumnModel(columns));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The default error message.
|
||||||
|
*/
|
||||||
|
public static String getDefaultErrorMessage() {
|
||||||
|
return DEFAULT_ERROR_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The default message for no results.
|
||||||
|
*/
|
||||||
|
public static String getDefaultNoResultsMessage() {
|
||||||
|
return DEFAULT_NO_RESULTS_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final JScrollPane tableScrollPane;
|
||||||
|
private final Overlay overlayLayer;
|
||||||
|
private final ListTableModel<T> tableModel;
|
||||||
|
private final JTable table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param tableModel The model to use for the table.
|
||||||
|
*/
|
||||||
|
public JTablePanel(ListTableModel<T> tableModel) {
|
||||||
|
this.tableModel = tableModel;
|
||||||
|
this.table = new JTable(tableModel);
|
||||||
|
this.table.getTableHeader().setReorderingAllowed(false);
|
||||||
|
|
||||||
|
this.overlayLayer = new Overlay();
|
||||||
|
this.tableScrollPane = new JScrollPane(table);
|
||||||
|
JLayer<JComponent> dualLayer = new JLayer<JComponent>(tableScrollPane, overlayLayer);
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
add(dualLayer, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The underlying JTable's column model.
|
||||||
|
*/
|
||||||
|
public TableColumnModel getColumnModel() {
|
||||||
|
return this.table.getColumnModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the underlying JTable's column model.
|
||||||
|
*
|
||||||
|
* @param columnModel The table column model to use with the JTable.
|
||||||
|
*
|
||||||
|
* @return As a utility, returns this.
|
||||||
|
*/
|
||||||
|
public JTablePanel<T> setColumnModel(TableColumnModel columnModel) {
|
||||||
|
this.table.setColumnModel(columnModel);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the data to be shown in the JTable. Repaint is not handled in this
|
||||||
|
* method and should be handled separately.
|
||||||
|
*
|
||||||
|
* @param data The list of data objects to be shown.
|
||||||
|
*/
|
||||||
|
private void setResultList(List<T> data) {
|
||||||
|
// set the list of data to be shown as either the data or an empty list
|
||||||
|
// on null.
|
||||||
|
List<T> dataToSet = (data == null) ? Collections.emptyList() : data;
|
||||||
|
|
||||||
|
// since the data is being reset, scroll to the top.
|
||||||
|
tableScrollPane.getVerticalScrollBar().setValue(0);
|
||||||
|
|
||||||
|
// set the underlying table model's data.
|
||||||
|
this.tableModel.setDataRows(dataToSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the message and visibility of the overlay. Repaint is not handled in
|
||||||
|
* this method and should be handled separately.
|
||||||
|
*
|
||||||
|
* @param visible The visibility of the overlay.
|
||||||
|
* @param message The message in the overlay.
|
||||||
|
*/
|
||||||
|
private void setOverlay(boolean visible, String message) {
|
||||||
|
this.overlayLayer.setVisible(visible);
|
||||||
|
this.overlayLayer.setMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the results from the underlying JTable and shows the provided
|
||||||
|
* message.
|
||||||
|
*
|
||||||
|
* @param message The message to be shown.
|
||||||
|
*/
|
||||||
|
public synchronized void showMessage(String message) {
|
||||||
|
setResultList(null);
|
||||||
|
setOverlay(true, message);
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a default loading message on the table. This will clear any results
|
||||||
|
* in the table.
|
||||||
|
*/
|
||||||
|
public void showDefaultLoadingMessage() {
|
||||||
|
showMessage(DEFAULT_LOADING_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the list as rows of data in the table. If overlay message will be
|
||||||
|
* cleared if present.
|
||||||
|
*
|
||||||
|
* @param data The data to be shown where each item represents a row of
|
||||||
|
* data.
|
||||||
|
*/
|
||||||
|
public synchronized void showResults(List<T> data) {
|
||||||
|
setOverlay(false, null);
|
||||||
|
setResultList(data);
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the data in a DataFetchResult. If there was an error during the
|
||||||
|
* operation, the errorMessage will be displayed. If the operation completed
|
||||||
|
* successfully and no data is present, noResultsMessage will be shown.
|
||||||
|
* Otherwise, the data will be shown as rows in the table.
|
||||||
|
*
|
||||||
|
* @param result The DataFetchResult.
|
||||||
|
* @param errorMessage The error message to be shown in the event of an
|
||||||
|
* error.
|
||||||
|
* @param noResultsMessage The message to be shown if there are no results
|
||||||
|
* but the operation completed successfully.
|
||||||
|
*/
|
||||||
|
public void showDataFetchResult(DataFetchResult<List<T>> result, String errorMessage, String noResultsMessage) {
|
||||||
|
if (result == null) {
|
||||||
|
logger.log(Level.SEVERE, "Null data processor result received.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (result.getResultType()) {
|
||||||
|
case SUCCESS:
|
||||||
|
if (result.getData() == null || result.getData().isEmpty()) {
|
||||||
|
showMessage(noResultsMessage);
|
||||||
|
} else {
|
||||||
|
showResults(result.getData());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ERROR:
|
||||||
|
// if there is an error, log accordingly, set result list to
|
||||||
|
// empty and display error message
|
||||||
|
logger.log(Level.WARNING, "An exception was caused while results were loaded.", result.getException());
|
||||||
|
showMessage(errorMessage);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// an unknown loading state was specified. log accordingly.
|
||||||
|
logger.log(Level.SEVERE, "No known loading state was found in result.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the data in a DataFetchResult. If there was an error during the
|
||||||
|
* operation, the DEFAULT_ERROR_MESSAGE will be displayed. If the operation
|
||||||
|
* completed successfully and no data is present, DEFAULT_NO_RESULTS_MESSAGE
|
||||||
|
* will be shown. Otherwise, the data will be shown as rows in the table.
|
||||||
|
*
|
||||||
|
* @param result The DataFetchResult.
|
||||||
|
*/
|
||||||
|
public void showDataFetchResult(DataFetchResult<List<T>> result) {
|
||||||
|
showDataFetchResult(result, DEFAULT_ERROR_MESSAGE, DEFAULT_NO_RESULTS_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import javax.swing.table.TableModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to be used with the JTablePanel that specifies a TableModel to
|
||||||
|
* be used with the underlying JTable based on a list of object type T.
|
||||||
|
*/
|
||||||
|
public interface ListTableModel<T> extends TableModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The list of objects supporting the rows to be displayed in the
|
||||||
|
* table.
|
||||||
|
*/
|
||||||
|
List<T> getDataRows();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list of objects to be displayed in the table.
|
||||||
|
*
|
||||||
|
* @param dataRows The datarows to be displayed.
|
||||||
|
*/
|
||||||
|
void setDataRows(List<T> dataRows);
|
||||||
|
}
|
@ -16,17 +16,24 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||||
|
|
||||||
import javax.swing.table.DefaultTableModel;
|
import javax.swing.table.DefaultTableModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Table model where cells are not editable.
|
* A Table model where cells are not editable.
|
||||||
*/
|
*/
|
||||||
class NonEditableTableModel extends DefaultTableModel {
|
public class NonEditableTableModel extends DefaultTableModel {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
NonEditableTableModel(Object[][] data, Object[] columnNames) {
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param data The data to be displayed.
|
||||||
|
* @param columnNames The column names.
|
||||||
|
*/
|
||||||
|
public NonEditableTableModel(Object[][] data, Object[] columnNames) {
|
||||||
super(data, columnNames);
|
super(data, columnNames);
|
||||||
}
|
}
|
||||||
|
|
@ -16,7 +16,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@ -31,7 +31,7 @@ import org.sleuthkit.autopsy.guiutils.GrayableCellRenderer;
|
|||||||
* cell contains a NodeProperty the value of that NodeProperty sets text to
|
* cell contains a NodeProperty the value of that NodeProperty sets text to
|
||||||
* empty string if null.
|
* empty string if null.
|
||||||
*/
|
*/
|
||||||
final class RightAlignedTableCellRenderer extends GrayableCellRenderer {
|
public class RightAlignedTableCellRenderer extends GrayableCellRenderer {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a list of swing workers in sequential order. Also, provides the ability
|
||||||
|
* to reset or cancel a run.
|
||||||
|
*
|
||||||
|
* Based on:
|
||||||
|
* https://stackoverflow.com/questions/31580805/java-swingworker-one-after-another-and-update-gui
|
||||||
|
*/
|
||||||
|
public class SwingWorkerSequentialExecutor {
|
||||||
|
|
||||||
|
private final ExecutorService executorService = Executors.newFixedThreadPool(1);
|
||||||
|
private List<? extends SwingWorker<?, ?>> workers = Collections.emptyList();
|
||||||
|
private List<Future<?>> futures = Collections.emptyList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels currently running operations and starts running the new list of
|
||||||
|
* swing workers.
|
||||||
|
*
|
||||||
|
* @param submittedWorkers The list of submitted swing workers.
|
||||||
|
*/
|
||||||
|
public synchronized void submit(List<? extends SwingWorker<?, ?>> submittedWorkers) {
|
||||||
|
// cancel currently running operations
|
||||||
|
cancelRunning();
|
||||||
|
|
||||||
|
// if no workers, there is nothing to run
|
||||||
|
if (submittedWorkers == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.workers = new ArrayList<>(submittedWorkers);
|
||||||
|
|
||||||
|
// start running the workers and capture the futures if there is a need to cancel them.
|
||||||
|
this.futures = this.workers.stream()
|
||||||
|
.map((w) -> executorService.submit(w))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels currently running items.
|
||||||
|
*/
|
||||||
|
public synchronized void cancelRunning() {
|
||||||
|
futures.forEach((f) -> f.cancel(true));
|
||||||
|
workers = Collections.emptyList();
|
||||||
|
futures = Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user