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;
70 @Messages({
"BaseDataSourceSummaryPanel_goToArtifact=View Source Result",
71 "BaseDataSourceSummaryPanel_goToFile=View Source File in Directory"})
72 abstract class BaseDataSourceSummaryPanel extends JPanel {
74 private static final long serialVersionUID = 1L;
76 private static final Logger logger = Logger.getLogger(BaseDataSourceSummaryPanel.class.getName());
78 private final SwingWorkerSequentialExecutor executor =
new SwingWorkerSequentialExecutor();
79 private final EventUpdateHandler updateHandler;
80 private final List<UpdateGovernor> governors;
82 private Runnable notifyParentClose = null;
84 private DataSource dataSource;
92 private final UpdateGovernor updateGovernor =
new UpdateGovernor() {
101 private boolean isInDataSource(BlackboardArtifact art, DataSource ds) {
104 return (art.getDataSource() != null && art.getDataSource().getId() == ds.getId());
105 }
catch (TskCoreException ex) {
106 logger.log(Level.WARNING,
"There was an error fetching datasource for artifact.", ex);
112 public boolean isRefreshRequired(ModuleDataEvent evt) {
113 DataSource ds = getDataSource();
115 if (ds == null || evt == null) {
121 if (evt.getArtifacts() != null
122 && !evt.getArtifacts().isEmpty()
123 && !evt.getArtifacts().stream().anyMatch((art) -> isInDataSource(art, ds))) {
128 for (UpdateGovernor governor : governors) {
129 if (governor.isRefreshRequired(evt)) {
138 public boolean isRefreshRequired(ModuleContentEvent evt) {
139 DataSource ds = getDataSource();
141 if (ds == null || evt == null) {
148 if (evt.getSource() instanceof Content
149 && ((Content) evt.getSource()).getDataSource() != null
150 && ((Content) evt.getSource()).getDataSource().getId() != ds.getId()) {
153 }
catch (TskCoreException ex) {
155 logger.log(Level.WARNING,
"There was an error fetching datasource for content.", ex);
158 for (UpdateGovernor governor : governors) {
159 if (governor.isRefreshRequired(evt)) {
168 public boolean isRefreshRequired(AbstractFile file) {
169 DataSource currentDataSource = getDataSource();
170 if (currentDataSource == null || file == null) {
175 Long fileDsId = null;
177 Content fileDataSource = file.getDataSource();
178 fileDsId = fileDataSource.getId();
179 }
catch (TskCoreException ex) {
180 logger.log(Level.WARNING,
"Unable to get the datasource for newly added file", ex);
183 if (fileDsId != null && currentDataSource.getId() == fileDsId) {
184 for (UpdateGovernor governor : governors) {
185 if (governor.isRefreshRequired(file)) {
195 public boolean isRefreshRequired(IngestJobEvent evt) {
196 for (UpdateGovernor governor : governors) {
197 if (governor.isRefreshRequired(evt)) {
206 public boolean isRefreshRequiredForCaseEvent(PropertyChangeEvent evt) {
207 for (UpdateGovernor governor : governors) {
208 if (governor.isRefreshRequiredForCaseEvent(evt)) {
217 public Set<Case.Events> getCaseEventUpdates() {
219 return governors.stream()
220 .filter(governor -> governor.getCaseEventUpdates() != null)
221 .flatMap(governor -> governor.getCaseEventUpdates().stream())
222 .collect(Collectors.toSet());
226 public Set<IngestJobEvent> getIngestJobEventUpdates() {
228 return governors.stream()
229 .filter(governor -> governor.getIngestJobEventUpdates() != null)
230 .flatMap(governor -> governor.getIngestJobEventUpdates().stream())
231 .collect(Collectors.toSet());
241 protected BaseDataSourceSummaryPanel(UpdateGovernor... governors) {
242 this.governors = (governors == null) ? Collections.emptyList() : Arrays.asList(governors);
243 this.updateHandler =
new EventUpdateHandler(this::onRefresh, updateGovernor);
250 this.updateHandler.register();
260 protected MenuItem getArtifactNavigateItem(BlackboardArtifact artifact) {
261 if (artifact == null) {
265 return new DefaultMenuItem(
266 Bundle.BaseDataSourceSummaryPanel_goToArtifact(),
268 final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
271 if (dtc != null && artifact != null) {
272 dtc.viewArtifact(artifact);
279 private static final Pattern windowsDrivePattern = Pattern.compile(
"^[A-Za-z]\\:(.*)$");
288 private String normalizePath(String path) {
293 String trimmed = path.trim();
294 Matcher match = windowsDrivePattern.matcher(trimmed);
296 return FilenameUtils.normalize(match.group(1),
true);
298 return FilenameUtils.normalize(trimmed,
true);
308 protected MenuItem getFileNavigateItem(String path) {
309 if (StringUtils.isNotBlank(path)) {
310 Path p = Paths.get(path);
311 String fileName = normalizePath(p.getFileName().toString());
312 String directory = normalizePath(p.getParent().toString());
314 if (fileName != null && directory != null) {
316 List<AbstractFile> files = Case.getCurrentCaseThrows().getSleuthkitCase().findFiles(getDataSource(), fileName, directory);
317 if (CollectionUtils.isNotEmpty(files)) {
318 return getFileNavigateItem(files.get(0));
320 }
catch (TskCoreException | NoCurrentCaseException ex) {
321 logger.log(Level.WARNING,
"There was an error fetching file for path: " + path, ex);
336 protected MenuItem getFileNavigateItem(AbstractFile file) {
341 return new DefaultMenuItem(
342 Bundle.BaseDataSourceSummaryPanel_goToFile(),
344 new ViewContextAction(Bundle.BaseDataSourceSummaryPanel_goToFile(), file)
345 .actionPerformed(null);
354 public void close() {
355 executor.cancelRunning();
356 updateHandler.unregister();
364 synchronized void setDataSource(DataSource dataSource) {
365 this.dataSource = dataSource;
366 this.executor.cancelRunning();
367 onNewDataSource(this.dataSource);
373 protected void notifyParentClose() {
374 if (notifyParentClose != null) {
375 notifyParentClose.run();
384 void setParentCloseListener(Runnable action) {
385 notifyParentClose = action;
391 protected synchronized DataSource getDataSource() {
392 return this.dataSource;
401 protected void submit(List<? extends SwingWorker<?, ?>> workers) {
402 executor.submit(workers);
410 synchronized void onRefresh() {
412 fetchInformation(this.dataSource);
421 protected abstract void fetchInformation(DataSource dataSource);
431 protected void fetchInformation(List<DataFetchComponents<DataSource, ?>> dataFetchComponents, DataSource dataSource) {
432 if (dataSource == null || !Case.isCaseOpen()) {
433 dataFetchComponents.forEach((item) -> item.getResultHandler()
434 .accept(DataFetchResult.getSuccessResult(null)));
437 List<DataFetchWorker<?, ?>> workers = dataFetchComponents
439 .map((components) ->
new DataFetchWorker<>(components, dataSource))
440 .collect(Collectors.toList());
443 if (!workers.isEmpty()) {
454 protected abstract void onNewDataSource(DataSource dataSource);
462 abstract List<ExcelSheetExport> getExports(DataSource dataSource);
473 protected static <T> T getFetchResult(
474 DataFetcher<DataSource, T> dataFetcher,
475 String sheetName, DataSource ds) {
478 return dataFetcher.runQuery(ds);
479 }
catch (Exception ex) {
480 logger.log(Level.WARNING,
481 String.format(
"There was an error while acquiring data for exporting worksheet(s): '%s' for dataSource: %s",
482 sheetName == null ?
"<null>" : sheetName,
483 ds == null || ds.getName() == null ?
"<null>" : ds.getName()), ex);
519 return excelConverter.
convert(data);
520 }
catch (ExcelExportException ex) {
521 logger.log(Level.WARNING,
522 String.format(
"There was an error while preparing export of worksheet(s): '%s'",
523 sheetName == null ?
"<null>" : sheetName), ex);
538 protected static <T> ExcelSheetExport getExport(
539 DataFetcher<DataSource, T> dataFetcher, ExcelExportFunction<T> excelConverter,
540 String sheetName, DataSource ds) {
542 T data = getFetchResult(dataFetcher, sheetName, ds);
543 return convertToExcel(excelConverter, data, sheetName);
554 protected static <T, C extends ExcelCellModel> ExcelSheetExport getTableExport(List<ColumnModel<T, C>> columnsModel,
555 String sheetName, List<T> data) {
557 return convertToExcel((dataList) ->
new ExcelTableExport<T, C>(sheetName, columnsModel, dataList),
572 protected static <T, C extends ExcelCellModel> ExcelSheetExport getTableExport(
573 DataFetcher<DataSource, List<T>> dataFetcher, List<ColumnModel<T, C>> columnsModel,
574 String sheetName, DataSource ds) {
576 return getExport(dataFetcher,
577 (dataList) ->
new ExcelTableExport<T, C>(sheetName, columnsModel, dataList),
591 protected void onNewDataSource(
592 List<DataFetchComponents<DataSource, ?>> dataFetchComponents,
593 List<? extends LoadableComponent<?>> loadableComponents,
594 DataSource dataSource) {
597 if (dataSource == null || !Case.isCaseOpen()) {
598 dataFetchComponents.forEach((item) -> item.getResultHandler()
599 .accept(DataFetchResult.getSuccessResult(null)));
603 loadableComponents.forEach((table) -> table.showDefaultLoadingMessage());
605 fetchInformation(dataSource);
ExcelSheetExport convert(T data)