19 package org.sleuthkit.autopsy.datasourcesummary.ui;
21 import java.beans.PropertyChangeEvent;
22 import java.nio.file.Path;
23 import java.nio.file.Paths;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.List;
28 import java.util.logging.Level;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 import java.util.stream.Collectors;
32 import javax.swing.JPanel;
33 import javax.swing.SwingWorker;
34 import org.apache.commons.collections.CollectionUtils;
35 import org.apache.commons.io.FilenameUtils;
36 import org.apache.commons.lang3.StringUtils;
37 import org.openide.util.NbBundle.Messages;
65 @Messages({
"BaseDataSourceSummaryPanel_goToArtifact=View Source Result",
66 "BaseDataSourceSummaryPanel_goToFile=View Source File in Directory"})
67 abstract class BaseDataSourceSummaryPanel extends JPanel {
69 private static final long serialVersionUID = 1L;
71 private static final Logger logger = Logger.getLogger(BaseDataSourceSummaryPanel.class.getName());
73 private final SwingWorkerSequentialExecutor executor =
new SwingWorkerSequentialExecutor();
74 private final EventUpdateHandler updateHandler;
75 private final List<UpdateGovernor> governors;
77 private Runnable notifyParentClose = null;
79 private DataSource dataSource;
87 private final UpdateGovernor updateGovernor =
new UpdateGovernor() {
96 private boolean isInDataSource(BlackboardArtifact art, DataSource ds) {
99 return (art.getDataSource() != null && art.getDataSource().getId() == ds.getId());
100 }
catch (TskCoreException ex) {
101 logger.log(Level.WARNING,
"There was an error fetching datasource for artifact.", ex);
107 public boolean isRefreshRequired(ModuleDataEvent evt) {
108 DataSource ds = getDataSource();
110 if (ds == null || evt == null) {
116 if (evt.getArtifacts() != null
117 && !evt.getArtifacts().isEmpty()
118 && !evt.getArtifacts().stream().anyMatch((art) -> isInDataSource(art, ds))) {
123 for (UpdateGovernor governor : governors) {
124 if (governor.isRefreshRequired(evt)) {
133 public boolean isRefreshRequired(ModuleContentEvent evt) {
134 DataSource ds = getDataSource();
136 if (ds == null || evt == null) {
143 if (evt.getSource() instanceof Content
144 && ((Content) evt.getSource()).getDataSource() != null
145 && ((Content) evt.getSource()).getDataSource().getId() != ds.getId()) {
148 }
catch (TskCoreException ex) {
150 logger.log(Level.WARNING,
"There was an error fetching datasource for content.", ex);
153 for (UpdateGovernor governor : governors) {
154 if (governor.isRefreshRequired(evt)) {
163 public boolean isRefreshRequired(AbstractFile file) {
164 DataSource currentDataSource = getDataSource();
165 if (currentDataSource == null || file == null) {
170 Long fileDsId = null;
172 Content fileDataSource = file.getDataSource();
173 fileDsId = fileDataSource.getId();
174 }
catch (TskCoreException ex) {
175 logger.log(Level.WARNING,
"Unable to get the datasource for newly added file", ex);
178 if (fileDsId != null && currentDataSource.getId() == fileDsId) {
179 for (UpdateGovernor governor : governors) {
180 if (governor.isRefreshRequired(file)) {
190 public boolean isRefreshRequired(IngestJobEvent evt) {
191 for (UpdateGovernor governor : governors) {
192 if (governor.isRefreshRequired(evt)) {
201 public boolean isRefreshRequiredForCaseEvent(PropertyChangeEvent evt) {
202 for (UpdateGovernor governor : governors) {
203 if (governor.isRefreshRequiredForCaseEvent(evt)) {
212 public Set<Case.Events> getCaseEventUpdates() {
214 return governors.stream()
215 .filter(governor -> governor.getCaseEventUpdates() != null)
216 .flatMap(governor -> governor.getCaseEventUpdates().stream())
217 .collect(Collectors.toSet());
221 public Set<IngestJobEvent> getIngestJobEventUpdates() {
223 return governors.stream()
224 .filter(governor -> governor.getIngestJobEventUpdates() != null)
225 .flatMap(governor -> governor.getIngestJobEventUpdates().stream())
226 .collect(Collectors.toSet());
236 protected BaseDataSourceSummaryPanel(UpdateGovernor... governors) {
237 this.governors = (governors == null) ? Collections.emptyList() : Arrays.asList(governors);
238 this.updateHandler =
new EventUpdateHandler(this::onRefresh, updateGovernor);
245 this.updateHandler.register();
255 protected MenuItem getArtifactNavigateItem(BlackboardArtifact artifact) {
256 if (artifact == null) {
260 return new DefaultMenuItem(
261 Bundle.BaseDataSourceSummaryPanel_goToArtifact(),
263 final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
266 if (dtc != null && artifact != null) {
267 dtc.viewArtifact(artifact);
274 private static final Pattern windowsDrivePattern = Pattern.compile(
"^[A-Za-z]\\:(.*)$");
283 private String normalizePath(String path) {
288 String trimmed = path.trim();
289 Matcher match = windowsDrivePattern.matcher(trimmed);
291 return FilenameUtils.normalize(match.group(1),
true);
293 return FilenameUtils.normalize(trimmed,
true);
303 protected MenuItem getFileNavigateItem(String path) {
304 if (StringUtils.isNotBlank(path)) {
305 Path p = Paths.get(path);
306 String fileName = normalizePath(p.getFileName().toString());
307 String directory = normalizePath(p.getParent().toString());
309 if (fileName != null && directory != null) {
311 List<AbstractFile> files = Case.getCurrentCaseThrows().getSleuthkitCase().findFiles(getDataSource(), fileName, directory);
312 if (CollectionUtils.isNotEmpty(files)) {
313 return getFileNavigateItem(files.get(0));
315 }
catch (TskCoreException | NoCurrentCaseException ex) {
316 logger.log(Level.WARNING,
"There was an error fetching file for path: " + path, ex);
331 protected MenuItem getFileNavigateItem(AbstractFile file) {
336 return new DefaultMenuItem(
337 Bundle.BaseDataSourceSummaryPanel_goToFile(),
339 new ViewContextAction(Bundle.BaseDataSourceSummaryPanel_goToFile(), file)
340 .actionPerformed(null);
349 public void close() {
350 executor.cancelRunning();
351 updateHandler.unregister();
359 synchronized void setDataSource(DataSource dataSource) {
360 this.dataSource = dataSource;
361 this.executor.cancelRunning();
362 onNewDataSource(this.dataSource);
368 protected void notifyParentClose() {
369 if (notifyParentClose != null) {
370 notifyParentClose.run();
379 void setParentCloseListener(Runnable action) {
380 notifyParentClose = action;
386 protected synchronized DataSource getDataSource() {
387 return this.dataSource;
396 protected void submit(List<? extends SwingWorker<?, ?>> workers) {
397 executor.submit(workers);
405 synchronized void onRefresh() {
407 fetchInformation(this.dataSource);
416 protected abstract void fetchInformation(DataSource dataSource);
426 protected void fetchInformation(List<DataFetchComponents<DataSource, ?>> dataFetchComponents, DataSource dataSource) {
427 if (dataSource == null || !Case.isCaseOpen()) {
428 dataFetchComponents.forEach((item) -> item.getResultHandler()
429 .accept(DataFetchResult.getSuccessResult(null)));
432 List<DataFetchWorker<?, ?>> workers = dataFetchComponents
434 .map((components) ->
new DataFetchWorker<>(components, dataSource))
435 .collect(Collectors.toList());
438 if (!workers.isEmpty()) {
449 protected abstract void onNewDataSource(DataSource dataSource);
460 protected static <T> T getFetchResult(
461 DataFetcher<DataSource, T> dataFetcher,
462 String sheetName, DataSource ds) {
465 return dataFetcher.runQuery(ds);
466 }
catch (Exception ex) {
467 logger.log(Level.WARNING,
468 String.format(
"There was an error while acquiring data for exporting worksheet(s): '%s' for dataSource: %s",
469 sheetName == null ?
"<null>" : sheetName,
470 ds == null || ds.getName() == null ?
"<null>" : ds.getName()), ex);
484 protected void onNewDataSource(
485 List<DataFetchComponents<DataSource, ?>> dataFetchComponents,
486 List<? extends LoadableComponent<?>> loadableComponents,
487 DataSource dataSource) {
490 if (dataSource == null || !Case.isCaseOpen()) {
491 dataFetchComponents.forEach((item) -> item.getResultHandler()
492 .accept(DataFetchResult.getSuccessResult(null)));
496 loadableComponents.forEach((table) -> table.showDefaultLoadingMessage());
498 fetchInformation(dataSource);