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);