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;
63 @Messages({
"BaseDataSourceSummaryPanel_goToArtifact=View Source Result",
64 "BaseDataSourceSummaryPanel_goToFile=View Source File in Directory"})
65 abstract class BaseDataSourceSummaryPanel extends JPanel {
67 private static final long serialVersionUID = 1L;
69 private static final Logger logger = Logger.getLogger(BaseDataSourceSummaryPanel.class.getName());
71 private final SwingWorkerSequentialExecutor executor =
new SwingWorkerSequentialExecutor();
72 private final EventUpdateHandler updateHandler;
73 private final List<UpdateGovernor> governors;
75 private Runnable notifyParentClose = null;
77 private DataSource dataSource;
85 private final UpdateGovernor updateGovernor =
new UpdateGovernor() {
94 private boolean isInDataSource(BlackboardArtifact art, DataSource ds) {
97 return (art.getDataSource() != null && art.getDataSource().getId() == ds.getId());
98 }
catch (TskCoreException ex) {
99 logger.log(Level.WARNING,
"There was an error fetching datasource for artifact.", ex);
105 public boolean isRefreshRequired(ModuleDataEvent evt) {
106 DataSource ds = getDataSource();
108 if (ds == null || evt == null) {
114 if (evt.getArtifacts() != null
115 && !evt.getArtifacts().isEmpty()
116 && !evt.getArtifacts().stream().anyMatch((art) -> isInDataSource(art, ds))) {
121 for (UpdateGovernor governor : governors) {
122 if (governor.isRefreshRequired(evt)) {
131 public boolean isRefreshRequired(ModuleContentEvent evt) {
132 DataSource ds = getDataSource();
134 if (ds == null || evt == null) {
141 if (evt.getSource() instanceof Content
142 && ((Content) evt.getSource()).getDataSource() != null
143 && ((Content) evt.getSource()).getDataSource().getId() != ds.getId()) {
146 }
catch (TskCoreException ex) {
148 logger.log(Level.WARNING,
"There was an error fetching datasource for content.", ex);
151 for (UpdateGovernor governor : governors) {
152 if (governor.isRefreshRequired(evt)) {
161 public boolean isRefreshRequired(AbstractFile file) {
162 DataSource currentDataSource = getDataSource();
163 if (currentDataSource == null || file == null) {
168 Long fileDsId = null;
170 Content fileDataSource = file.getDataSource();
171 fileDsId = fileDataSource.getId();
172 }
catch (TskCoreException ex) {
173 logger.log(Level.WARNING,
"Unable to get the datasource for newly added file", ex);
176 if (fileDsId != null && currentDataSource.getId() == fileDsId) {
177 for (UpdateGovernor governor : governors) {
178 if (governor.isRefreshRequired(file)) {
188 public boolean isRefreshRequired(IngestJobEvent evt) {
189 for (UpdateGovernor governor : governors) {
190 if (governor.isRefreshRequired(evt)) {
199 public boolean isRefreshRequiredForCaseEvent(PropertyChangeEvent evt) {
200 for (UpdateGovernor governor : governors) {
201 if (governor.isRefreshRequiredForCaseEvent(evt)) {
210 public Set<Case.Events> getCaseEventUpdates() {
212 return governors.stream()
213 .filter(governor -> governor.getCaseEventUpdates() != null)
214 .flatMap(governor -> governor.getCaseEventUpdates().stream())
215 .collect(Collectors.toSet());
219 public Set<IngestJobEvent> getIngestJobEventUpdates() {
221 return governors.stream()
222 .filter(governor -> governor.getIngestJobEventUpdates() != null)
223 .flatMap(governor -> governor.getIngestJobEventUpdates().stream())
224 .collect(Collectors.toSet());
234 protected BaseDataSourceSummaryPanel(UpdateGovernor... governors) {
235 this.governors = (governors == null) ? Collections.emptyList() : Arrays.asList(governors);
236 this.updateHandler =
new EventUpdateHandler(this::onRefresh, updateGovernor);
237 this.updateHandler.register();
247 protected CellModelTableCellRenderer.MenuItem getArtifactNavigateItem(BlackboardArtifact artifact) {
248 if (artifact == null) {
252 return new CellModelTableCellRenderer.DefaultMenuItem(
253 Bundle.BaseDataSourceSummaryPanel_goToArtifact(),
255 final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
258 if (dtc != null && artifact != null) {
259 dtc.viewArtifact(artifact);
266 private static final Pattern windowsDrivePattern = Pattern.compile(
"^[A-Za-z]\\:(.*)$");
275 private String normalizePath(String path) {
280 String trimmed = path.trim();
281 Matcher match = windowsDrivePattern.matcher(trimmed);
283 return FilenameUtils.normalize(match.group(1),
true);
285 return FilenameUtils.normalize(trimmed,
true);
295 protected CellModelTableCellRenderer.MenuItem getFileNavigateItem(String path) {
296 if (StringUtils.isNotBlank(path)) {
297 Path p = Paths.get(path);
298 String fileName = normalizePath(p.getFileName().toString());
299 String directory = normalizePath(p.getParent().toString());
301 if (fileName != null && directory != null) {
303 List<AbstractFile> files = Case.getCurrentCaseThrows().getSleuthkitCase().findFiles(getDataSource(), fileName, directory);
304 if (CollectionUtils.isNotEmpty(files)) {
305 return getFileNavigateItem(files.get(0));
307 }
catch (TskCoreException | NoCurrentCaseException ex) {
308 logger.log(Level.WARNING,
"There was an error fetching file for path: " + path, ex);
323 protected CellModelTableCellRenderer.MenuItem getFileNavigateItem(AbstractFile file) {
328 return new CellModelTableCellRenderer.DefaultMenuItem(
329 Bundle.BaseDataSourceSummaryPanel_goToFile(),
331 new ViewContextAction(Bundle.BaseDataSourceSummaryPanel_goToFile(), file)
332 .actionPerformed(null);
341 public void close() {
342 executor.cancelRunning();
343 updateHandler.unregister();
351 synchronized void setDataSource(DataSource dataSource) {
352 this.dataSource = dataSource;
353 this.executor.cancelRunning();
354 onNewDataSource(this.dataSource);
360 protected void notifyParentClose() {
361 if (notifyParentClose != null) {
362 notifyParentClose.run();
371 void setParentCloseListener(Runnable action) {
372 notifyParentClose = action;
378 protected synchronized DataSource getDataSource() {
379 return this.dataSource;
388 protected void submit(List<? extends SwingWorker<?, ?>> workers) {
389 executor.submit(workers);
397 synchronized void onRefresh() {
399 fetchInformation(this.dataSource);
408 protected abstract void fetchInformation(DataSource dataSource);
418 protected void fetchInformation(List<DataFetchComponents<DataSource, ?>> dataFetchComponents, DataSource dataSource) {
419 if (dataSource == null || !Case.isCaseOpen()) {
420 dataFetchComponents.forEach((item) -> item.getResultHandler()
421 .accept(DataFetchResult.getSuccessResult(null)));
424 List<DataFetchWorker<?, ?>> workers = dataFetchComponents
426 .map((components) ->
new DataFetchWorker<>(components, dataSource))
427 .collect(Collectors.toList());
430 if (!workers.isEmpty()) {
441 protected abstract void onNewDataSource(DataSource dataSource);
452 protected void onNewDataSource(
453 List<DataFetchComponents<DataSource, ?>> dataFetchComponents,
454 List<? extends LoadableComponent<?>> loadableComponents,
455 DataSource dataSource) {
458 if (dataSource == null || !Case.isCaseOpen()) {
459 dataFetchComponents.forEach((item) -> item.getResultHandler()
460 .accept(DataFetchResult.getSuccessResult(null)));
464 loadableComponents.forEach((table) -> table.showDefaultLoadingMessage());
466 fetchInformation(dataSource);