Autopsy  4.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
CountsViewPane.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2014 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.timeline.ui.countsview;
20 
21 import com.google.common.collect.Lists;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.function.Function;
26 import javafx.application.Platform;
27 import javafx.beans.Observable;
28 import javafx.beans.property.SimpleObjectProperty;
29 import javafx.collections.FXCollections;
30 import javafx.concurrent.Task;
31 import javafx.fxml.FXML;
32 import javafx.scene.Node;
33 import javafx.scene.chart.CategoryAxis;
34 import javafx.scene.chart.NumberAxis;
35 import javafx.scene.chart.StackedBarChart;
36 import javafx.scene.chart.XYChart;
37 import javafx.scene.control.Label;
38 import javafx.scene.control.RadioButton;
39 import javafx.scene.control.ToggleGroup;
40 import javafx.scene.control.Tooltip;
41 import javafx.scene.effect.Effect;
42 import javafx.scene.layout.HBox;
43 import javafx.scene.layout.Pane;
44 import javafx.scene.layout.Region;
45 import org.joda.time.Interval;
46 import org.openide.util.NbBundle;
55 
77 public class CountsViewPane extends AbstractVisualizationPane<String, Number, Node, EventCountsChart> {
78 
79  private static final Logger LOGGER = Logger.getLogger(CountsViewPane.class.getName());
80 
81  private final NumberAxis countAxis = new NumberAxis();
82  private final CategoryAxis dateAxis = new CategoryAxis(FXCollections.<String>observableArrayList());
83 
84  private final SimpleObjectProperty<ScaleType> scale = new SimpleObjectProperty<>(ScaleType.LOGARITHMIC);
85 
86  @Override
87  protected String getTickMarkLabel(String labelValueString) {
88  return labelValueString;
89  }
90 
91  @Override
92  protected Boolean isTickBold(String value) {
93  return dataSeries.stream().flatMap((series) -> series.getData().stream())
94  .anyMatch((data) -> data.getXValue().equals(value) && data.getYValue().intValue() > 0);
95  }
96 
97  @Override
98  protected Task<Boolean> getUpdateTask() {
99  return new CountsUpdateTask();
100  }
101 
102  public CountsViewPane(TimeLineController controller, Pane partPane, Pane contextPane, Region spacer) {
103  super(controller, partPane, contextPane, spacer);
104  chart = new EventCountsChart(controller, dateAxis, countAxis, selectedNodes);
106  chart.setData(dataSeries);
107  setCenter(chart);
108 
109  Tooltip.install(chart, getDefaultTooltip());
110 
111  settingsNodes = new ArrayList<>(new CountsViewSettingsPane().getChildrenUnmodifiable());
112 
113  dateAxis.getTickMarks().addListener((Observable observable) -> {
115  });
116  dateAxis.categorySpacingProperty().addListener((Observable observable) -> {
118  });
119  dateAxis.getCategories().addListener((Observable observable) -> {
121  });
122 
123  spacer.minWidthProperty().bind(countAxis.widthProperty().add(countAxis.tickLengthProperty()).add(dateAxis.startMarginProperty().multiply(2)));
124  spacer.prefWidthProperty().bind(countAxis.widthProperty().add(countAxis.tickLengthProperty()).add(dateAxis.startMarginProperty().multiply(2)));
125  spacer.maxWidthProperty().bind(countAxis.widthProperty().add(countAxis.tickLengthProperty()).add(dateAxis.startMarginProperty().multiply(2)));
126 
127  scale.addListener(o -> {
128  countAxis.tickLabelsVisibleProperty().bind(scale.isEqualTo(ScaleType.LINEAR));
129  countAxis.tickMarkVisibleProperty().bind(scale.isEqualTo(ScaleType.LINEAR));
130  countAxis.minorTickVisibleProperty().bind(scale.isEqualTo(ScaleType.LINEAR));
131  update();
132  });
133 
134  }
135 
136  @Override
137  protected NumberAxis getYAxis() {
138  return countAxis;
139  }
140 
141  @Override
142  protected CategoryAxis getXAxis() {
143  return dateAxis;
144  }
145 
146  @Override
147  protected double getTickSpacing() {
148  return dateAxis.getCategorySpacing();
149  }
150 
151  @Override
152  protected Effect getSelectionEffect() {
153  return chart.getSelectionEffect();
154  }
155 
156  @Override
157  protected void applySelectionEffect(Node c1, Boolean applied) {
158  if (applied) {
159  c1.setEffect(getSelectionEffect());
160  } else {
161  c1.setEffect(null);
162 
163  }
164  }
165 
166  private class CountsViewSettingsPane extends HBox {
167 
168  @FXML
169  private RadioButton logRadio;
170 
171  @FXML
172  private RadioButton linearRadio;
173 
174  @FXML
175  private ToggleGroup scaleGroup;
176 
177  @FXML
178  private Label scaleLabel;
179 
180  @FXML
181  void initialize() {
182  assert logRadio != null : "fx:id=\"logRadio\" was not injected: check your FXML file 'CountsViewSettingsPane.fxml'."; // NON-NLS
183  assert linearRadio != null : "fx:id=\"linearRadio\" was not injected: check your FXML file 'CountsViewSettingsPane.fxml'."; // NON-NLS
184  logRadio.setSelected(true);
185  scaleGroup.selectedToggleProperty().addListener(observable -> {
186  if (scaleGroup.getSelectedToggle() == linearRadio) {
187  scale.set(ScaleType.LINEAR);
188  }
189  if (scaleGroup.getSelectedToggle() == logRadio) {
190  scale.set(ScaleType.LOGARITHMIC);
191  }
192  });
193 
194  logRadio.setText(NbBundle.getMessage(CountsViewPane.class, "CountsViewPane.logRadio.text"));
195  linearRadio.setText(NbBundle.getMessage(CountsViewPane.class, "CountsViewPane.linearRadio.text"));
196  scaleLabel.setText(NbBundle.getMessage(CountsViewPane.class, "CountsViewPane.scaleLabel.text"));
197  }
198 
200  FXMLConstructor.construct(this, "CountsViewSettingsPane.fxml"); // NON-NLS
201  }
202  }
203 
204  @Override
205  protected void resetData() {
206 
207  Platform.runLater(() -> {
208  for (XYChart.Series<String, Number> s : dataSeries) {
209  s.getData().clear();
210  }
211 
212  dataSeries.clear();
213  eventTypeToSeriesMap.clear();
214  createSeries();
215  });
216  }
217 
218  private static enum ScaleType implements Function<Long, Double> {
219 
220  LINEAR(Long::doubleValue),
221  LOGARITHMIC(t -> Math.log10(t) + 1);
222 
223  private final Function<Long, Double> func;
224 
225  ScaleType(Function<Long, Double> func) {
226  this.func = func;
227  }
228 
229  @Override
230  public Double apply(Long t) {
231  return func.apply(t);
232  }
233  }
234 
235  @NbBundle.Messages({
236  "CountsViewPane.loggedTask.name=Updating Counts View",
237  "CountsViewPane.loggedTask.updatingCounts=Populating visualization"})
238  private class CountsUpdateTask extends VisualizationUpdateTask<List<String>> {
239 
240  CountsUpdateTask() {
241  super(Bundle.CountsViewPane_loggedTask_name(), true);
242  }
243 
244  @Override
245  protected Boolean call() throws Exception {
246  super.call();
247  if (isCancelled()) {
248  return null;
249  }
250 
252  chart.setRangeInfo(rangeInfo); //do we need this. It seems like a hack.
253 
254  List<Interval> intervals = rangeInfo.getIntervals();
255  List<String> categories = Lists.transform(intervals, rangeInfo::formatForTick);
256 
257  //clear old data, and reset ranges and series
258  resetChart(categories);
259 
260  updateMessage(Bundle.CountsViewPane_loggedTask_updatingCounts());
261  int chartMax = 0;
262  int numIntervals = intervals.size();
263  /*
264  * for each interval query database for event counts and add to
265  * chart.
266  *
267  * Doing this in chunks might seem inefficient but it lets us reuse
268  * more cached results as the user navigates to overlapping viewws
269  *
270  * //TODO: implement similar chunked caching in DetailsView -jm
271  */
272  for (int i = 0; i < numIntervals; i++) {
273  if (isCancelled()) {
274  return null;
275  }
276  updateProgress(i, numIntervals);
277  final Interval interval = intervals.get(i);
278  int maxPerInterval = 0;
279 
280  //query for current interval
281  Map<EventType, Long> eventCounts = filteredEvents.getEventCounts(interval);
282 
283  //for each type add data to graph
284  for (final EventType eventType : eventCounts.keySet()) {
285  if (isCancelled()) {
286  return null;
287  }
288 
289  final Long count = eventCounts.get(eventType);
290  if (count > 0) {
291  final String intervalCategory = rangeInfo.formatForTick(interval);
292  final double adjustedCount = scale.get().apply(count);
293 
294  final XYChart.Data<String, Number> dataItem =
295  new XYChart.Data<>(intervalCategory, adjustedCount,
296  new EventCountsChart.ExtraData(interval, eventType, count));
297  Platform.runLater(() -> getSeries(eventType).getData().add(dataItem));
298  maxPerInterval += adjustedCount;
299  }
300  }
301  chartMax = Math.max(chartMax, maxPerInterval);
302  }
303  //adjust vertical axis according to scale type and max counts
304  double countAxisUpperbound = 1 + chartMax * 1.2;
305  double tickUnit = ScaleType.LINEAR.equals(scale.get())
306  ? Math.pow(10, Math.max(0, Math.floor(Math.log10(chartMax)) - 1))
307  : Double.MAX_VALUE;
308  Platform.runLater(() -> {
309  countAxis.setTickUnit(tickUnit);
310  countAxis.setUpperBound(countAxisUpperbound);
311  });
312  return chartMax > 0; // are there events
313  }
314 
315  @Override
316  protected void setDateAxisValues(List<String> categories) {
317  dateAxis.getCategories().setAll(categories);
318  }
319  }
320 }
final Map< EventType, XYChart.Series< X, Y > > eventTypeToSeriesMap
Map< EventType, Long > getEventCounts(Interval timeRange)
static RangeDivisionInfo getRangeDivisionInfo(Interval timeRange)
final XYChart.Series< X, Y > getSeries(final EventType et)
synchronized static Logger getLogger(String name)
Definition: Logger.java:166
static void construct(Node node, String fxmlFileName)
CountsViewPane(TimeLineController controller, Pane partPane, Pane contextPane, Region spacer)

Copyright © 2012-2015 Basis Technology. Generated on: Wed Apr 6 2016
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.