Autopsy  4.18.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
TimelinePanel.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2020 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.datasourcesummary.ui;
20 
21 import java.awt.Color;
22 import java.text.DateFormat;
23 import java.text.SimpleDateFormat;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.Date;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.logging.Level;
31 import org.apache.commons.collections.CollectionUtils;
32 import org.joda.time.DateTime;
33 import org.joda.time.Interval;
34 import org.openide.util.NbBundle.Messages;
35 import org.openide.util.actions.CallableSystemAction;
62 import org.sleuthkit.datamodel.DataSource;
63 import org.sleuthkit.datamodel.TskCoreException;
64 
69 @Messages({
70  "TimelinePanel_earliestLabel_title=Earliest",
71  "TimelinePanel_latestLabel_title=Latest",
72  "TimlinePanel_last30DaysChart_title=Last 30 Days",
73  "TimlinePanel_last30DaysChart_fileEvts_title=File Events",
74  "TimlinePanel_last30DaysChart_artifactEvts_title=Result Events",})
75 public class TimelinePanel extends BaseDataSourceSummaryPanel {
76 
77  private static final Logger logger = Logger.getLogger(TimelinePanel.class.getName());
78  private static final long serialVersionUID = 1L;
79 
80  private static final String EARLIEST_LATEST_FORMAT_STR = "MMM d, yyyy";
81  private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat(EARLIEST_LATEST_FORMAT_STR);
82  private static final DateFormat CHART_FORMAT = getUtcFormat("MMM d, yyyy");
83  private static final int MOST_RECENT_DAYS_COUNT = 30;
84 
91  private static DateFormat getUtcFormat(String formatString) {
92  return new SimpleDateFormat(formatString, Locale.getDefault());
93  }
94 
95  // components displayed in the tab
96  private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
97  private final LoadableLabel earliestLabel = new LoadableLabel(Bundle.TimelinePanel_earliestLabel_title());
98  private final LoadableLabel latestLabel = new LoadableLabel(Bundle.TimelinePanel_latestLabel_title());
99  private final BarChartPanel last30DaysChart = new BarChartPanel(Bundle.TimlinePanel_last30DaysChart_title(), "", "");
101 
102  // all loadable components on this tab
103  private final List<LoadableComponent<?>> loadableComponents = Arrays.asList(earliestLabel, latestLabel, last30DaysChart);
104 
106 
107  // actions to load data for this tab
108  private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
109 
110  public TimelinePanel() {
111  this(new TimelineSummary());
112  }
113 
117  public TimelinePanel(TimelineSummary timelineData) {
118  super(timelineData);
119 
120  dataFetcher = (dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT);
121 
122  // set up data acquisition methods
123  dataFetchComponents = Arrays.asList(
124  new DataFetchWorker.DataFetchComponents<>(dataFetcher, (result) -> handleResult(result)));
125 
126  initComponents();
127  }
128 
138  private static String formatDate(Date date, DateFormat formatter) {
139  return date == null ? null : formatter.format(date);
140  }
141 
142  private static final Color FILE_EVT_COLOR = new Color(228, 22, 28);
143  private static final Color ARTIFACT_EVT_COLOR = new Color(21, 227, 100);
144 
154  private List<BarChartSeries> parseChartData(List<DailyActivityAmount> recentDaysActivity, boolean showIntermediateDates) {
155  // if no data, return null indicating no result.
156  if (CollectionUtils.isEmpty(recentDaysActivity)) {
157  return null;
158  }
159 
160  // Create a bar chart item for each recent days activity item
161  List<BarChartItem> fileEvtCounts = new ArrayList<>();
162  List<BarChartItem> artifactEvtCounts = new ArrayList<>();
163 
164  for (int i = 0; i < recentDaysActivity.size(); i++) {
165  DailyActivityAmount curItem = recentDaysActivity.get(i);
166 
167  long fileAmt = curItem.getFileActivityCount();
168  long artifactAmt = curItem.getArtifactActivityCount() * 100;
169  String formattedDate = (showIntermediateDates || i == 0 || i == recentDaysActivity.size() - 1)
170  ? formatDate(curItem.getDay(), CHART_FORMAT) : "";
171 
172  OrderedKey thisKey = new OrderedKey(formattedDate, i);
173  fileEvtCounts.add(new BarChartItem(thisKey, fileAmt));
174  artifactEvtCounts.add(new BarChartItem(thisKey, artifactAmt));
175  }
176 
177  return Arrays.asList(
178  new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_fileEvts_title(), FILE_EVT_COLOR, fileEvtCounts),
179  new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_artifactEvts_title(), ARTIFACT_EVT_COLOR, artifactEvtCounts));
180  }
181 
182  private final Object timelineBtnLock = new Object();
183  private TimelineSummaryData curTimelineData = null;
184 
194  earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT)));
195  latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT)));
196  last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity(), false)));
197 
198  if (result != null
200  && result.getData() != null) {
201 
202  synchronized (this.timelineBtnLock) {
203  this.curTimelineData = result.getData();
204  this.viewInTimelineBtn.setEnabled(true);
205  }
206  } else {
207  synchronized (this.timelineBtnLock) {
208  this.viewInTimelineBtn.setEnabled(false);
209  }
210  }
211  }
212 
216  private void openFilteredChart() {
217  DataSource dataSource = null;
218  Date minDate = null;
219  Date maxDate = null;
220 
221  // get date from current timelineData if that data exists.
222  synchronized (this.timelineBtnLock) {
223  if (curTimelineData == null) {
224  return;
225  }
226 
227  dataSource = curTimelineData.getDataSource();
228  if (CollectionUtils.isNotEmpty(curTimelineData.getMostRecentDaysActivity())) {
229  minDate = curTimelineData.getMostRecentDaysActivity().get(0).getDay();
230  maxDate = curTimelineData.getMostRecentDaysActivity().get(curTimelineData.getMostRecentDaysActivity().size() - 1).getDay();
231  // set outer bound to end of day instead of beginning
232  if (maxDate != null) {
233  maxDate = new Date(maxDate.getTime() + 1000 * 60 * 60 * 24);
234  }
235  }
236  }
237 
238  openFilteredChart(dataSource, minDate, maxDate);
239  }
240 
248  private void openFilteredChart(DataSource dataSource, Date minDate, Date maxDate) {
249  OpenTimelineAction openTimelineAction = CallableSystemAction.get(OpenTimelineAction.class);
250  if (openTimelineAction == null) {
251  logger.log(Level.WARNING, "No OpenTimelineAction provided by CallableSystemAction; taking no redirect action.");
252  }
253 
254  // notify dialog (if in dialog) should close.
255  TimelinePanel.this.notifyParentClose();
256 
257  Interval timeSpan = null;
258 
259  try {
260  final TimeLineController controller = TimeLineModule.getController();
261 
262  if (dataSource != null) {
263  controller.pushFilters(timelineUtils.getDataSourceFilterState(dataSource));
264  }
265 
266  if (minDate != null && maxDate != null) {
267  timeSpan = new Interval(new DateTime(minDate), new DateTime(maxDate));
268  }
269  } catch (NoCurrentCaseException | TskCoreException ex) {
270  logger.log(Level.WARNING, "Unable to view time range in Timeline view", ex);
271  }
272 
273  try {
274  openTimelineAction.showTimeline(timeSpan);
275  } catch (TskCoreException ex) {
276  logger.log(Level.WARNING, "An unexpected exception occurred while opening the timeline.", ex);
277  }
278  }
279 
280  @Override
281  protected void fetchInformation(DataSource dataSource) {
282  fetchInformation(dataFetchComponents, dataSource);
283  }
284 
285  @Override
286  protected void onNewDataSource(DataSource dataSource) {
287  onNewDataSource(dataFetchComponents, loadableComponents, dataSource);
288  }
289 
290  @Override
291  public void close() {
292  ingestRunningLabel.unregister();
293  super.close();
294  }
295 
303  private static DefaultCellModel<?> getEarliestLatestCell(Date date) {
304  return new DefaultCellModel<>(date, (dt) -> dt == null ? "" : EARLIEST_LATEST_FORMAT.format(dt), EARLIEST_LATEST_FORMAT_STR);
305  }
306 
307  @Messages({
308  "TimelinePanel_getExports_sheetName=Timeline",
309  "TimelinePanel_getExports_activityRange=Activity Range",
310  "TimelinePanel_getExports_earliest=Earliest:",
311  "TimelinePanel_getExports_latest=Latest:",
312  "TimelinePanel_getExports_dateColumnHeader=Date",
313  "TimelinePanel_getExports_chartName=Last 30 Days",})
314  @Override
315  List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
316  TimelineSummaryData summaryData = getFetchResult(dataFetcher, "Timeline", dataSource);
317  if (summaryData == null) {
318  return Collections.emptyList();
319  }
320 
321  return Arrays.asList(
322  new ExcelSpecialFormatExport(Bundle.TimelinePanel_getExports_sheetName(),
323  Arrays.asList(
324  new TitledExportable(Bundle.TimelinePanel_getExports_activityRange(), Collections.emptyList()),
325  new KeyValueItemExportable(Bundle.TimelinePanel_getExports_earliest(), getEarliestLatestCell(summaryData.getMinDate())),
326  new KeyValueItemExportable(Bundle.TimelinePanel_getExports_latest(), getEarliestLatestCell(summaryData.getMaxDate())),
327  new BarChartExport(Bundle.TimelinePanel_getExports_dateColumnHeader(),
328  "#,###",
329  Bundle.TimelinePanel_getExports_chartName(),
330  parseChartData(summaryData.getMostRecentDaysActivity(), true)))));
331  }
332 
338  @SuppressWarnings("unchecked")
339  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
340  private void initComponents() {
341 
342  javax.swing.JScrollPane mainScrollPane = new javax.swing.JScrollPane();
343  javax.swing.JPanel mainContentPanel = new javax.swing.JPanel();
344  javax.swing.JPanel ingestRunningPanel = ingestRunningLabel;
345  javax.swing.JLabel activityRangeLabel = new javax.swing.JLabel();
346  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));
347  javax.swing.JPanel earliestLabelPanel = earliestLabel;
348  javax.swing.JPanel latestLabelPanel = latestLabel;
349  javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20));
350  javax.swing.JPanel last30DaysPanel = last30DaysChart;
351  viewInTimelineBtn = new javax.swing.JButton();
352  javax.swing.Box.Filler filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
353 
354  mainContentPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
355  mainContentPanel.setLayout(new javax.swing.BoxLayout(mainContentPanel, javax.swing.BoxLayout.PAGE_AXIS));
356 
357  ingestRunningPanel.setAlignmentX(0.0F);
358  ingestRunningPanel.setMaximumSize(new java.awt.Dimension(32767, 25));
359  ingestRunningPanel.setMinimumSize(new java.awt.Dimension(10, 25));
360  ingestRunningPanel.setPreferredSize(new java.awt.Dimension(10, 25));
361  mainContentPanel.add(ingestRunningPanel);
362 
363  activityRangeLabel.setFont(new java.awt.Font("Segoe UI", 1, 12)); // NOI18N
364  org.openide.awt.Mnemonics.setLocalizedText(activityRangeLabel, org.openide.util.NbBundle.getMessage(TimelinePanel.class, "TimelinePanel.activityRangeLabel.text")); // NOI18N
365  mainContentPanel.add(activityRangeLabel);
366  activityRangeLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(TimelinePanel.class, "PastCasesPanel.notableFileLabel.text")); // NOI18N
367 
368  filler1.setAlignmentX(0.0F);
369  mainContentPanel.add(filler1);
370 
371  earliestLabelPanel.setAlignmentX(0.0F);
372  earliestLabelPanel.setMaximumSize(new java.awt.Dimension(32767, 20));
373  earliestLabelPanel.setMinimumSize(new java.awt.Dimension(100, 20));
374  earliestLabelPanel.setPreferredSize(new java.awt.Dimension(100, 20));
375  mainContentPanel.add(earliestLabelPanel);
376 
377  latestLabelPanel.setAlignmentX(0.0F);
378  latestLabelPanel.setMaximumSize(new java.awt.Dimension(32767, 20));
379  latestLabelPanel.setMinimumSize(new java.awt.Dimension(100, 20));
380  latestLabelPanel.setPreferredSize(new java.awt.Dimension(100, 20));
381  mainContentPanel.add(latestLabelPanel);
382 
383  filler2.setAlignmentX(0.0F);
384  mainContentPanel.add(filler2);
385 
386  last30DaysPanel.setAlignmentX(0.0F);
387  last30DaysPanel.setMaximumSize(new java.awt.Dimension(600, 300));
388  last30DaysPanel.setMinimumSize(new java.awt.Dimension(600, 300));
389  last30DaysPanel.setPreferredSize(new java.awt.Dimension(600, 300));
390  last30DaysPanel.setVerifyInputWhenFocusTarget(false);
391  mainContentPanel.add(last30DaysPanel);
392 
393  org.openide.awt.Mnemonics.setLocalizedText(viewInTimelineBtn, org.openide.util.NbBundle.getMessage(TimelinePanel.class, "TimelinePanel.viewInTimelineBtn.text")); // NOI18N
394  viewInTimelineBtn.setEnabled(false);
395  viewInTimelineBtn.addActionListener(new java.awt.event.ActionListener() {
396  public void actionPerformed(java.awt.event.ActionEvent evt) {
397  viewInTimelineBtnActionPerformed(evt);
398  }
399  });
400  mainContentPanel.add(viewInTimelineBtn);
401 
402  filler5.setAlignmentX(0.0F);
403  mainContentPanel.add(filler5);
404 
405  mainScrollPane.setViewportView(mainContentPanel);
406 
407  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
408  this.setLayout(layout);
409  layout.setHorizontalGroup(
410  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
411  .addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
412  );
413  layout.setVerticalGroup(
414  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
415  .addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
416  );
417  }// </editor-fold>//GEN-END:initComponents
418 
419  private void viewInTimelineBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewInTimelineBtnActionPerformed
420  openFilteredChart();
421  }//GEN-LAST:event_viewInTimelineBtnActionPerformed
422 
423  // Variables declaration - do not modify//GEN-BEGIN:variables
424  private javax.swing.JButton viewInTimelineBtn;
425  // End of variables declaration//GEN-END:variables
426 
427 }
static DateFormat getUtcFormat(String formatString)
static< I, O > DataFetchResult< O > getSubResult(DataFetchResult< I > inputResult, Function< I, O > getSubResult)
synchronized void showTimeline(AbstractFile file, BlackboardArtifact artifact, Interval timeSpan)
final List< DataFetchComponents< DataSource,?> > dataFetchComponents
void showDataFetchResult(DataFetchResult< T > result, String errorMessage, String noResultsMessage)
void handleResult(DataFetchResult< TimelineSummaryData > result)
static String formatDate(Date date, DateFormat formatter)
void viewInTimelineBtnActionPerformed(java.awt.event.ActionEvent evt)
void openFilteredChart(DataSource dataSource, Date minDate, Date maxDate)
synchronized void pushFilters(RootFilterState filter)
final DataFetcher< DataSource, TimelineSummaryData > dataFetcher
static DefaultCellModel<?> getEarliestLatestCell(Date date)
TimelineSummaryData getData(DataSource dataSource, int recentDaysNum)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
List< BarChartSeries > parseChartData(List< DailyActivityAmount > recentDaysActivity, boolean showIntermediateDates)

Copyright © 2012-2021 Basis Technology. Generated on: Thu Jul 8 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.