Autopsy  4.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
EventClusterNode.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-15 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.detailview;
20 
21 import com.google.common.collect.Lists;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.List;
26 import static java.util.Objects.nonNull;
27 import java.util.concurrent.ExecutionException;
28 import java.util.logging.Level;
29 import java.util.stream.Collectors;
30 import javafx.beans.binding.Bindings;
31 import javafx.concurrent.Task;
32 import javafx.event.EventHandler;
33 import javafx.geometry.Pos;
34 import javafx.scene.Cursor;
35 import javafx.scene.control.Button;
36 import javafx.scene.image.Image;
37 import javafx.scene.image.ImageView;
38 import javafx.scene.input.MouseEvent;
39 import javafx.scene.layout.Border;
40 import javafx.scene.layout.BorderStroke;
41 import javafx.scene.layout.BorderStrokeStyle;
42 import javafx.scene.layout.BorderWidths;
43 import javafx.scene.layout.VBox;
44 import org.controlsfx.control.action.Action;
45 import org.controlsfx.control.action.ActionUtils;
46 import org.joda.time.DateTime;
47 import org.joda.time.Interval;
48 import org.openide.util.NbBundle;
57 import static org.sleuthkit.autopsy.timeline.ui.detailview.EventBundleNodeBase.configureLoDButton;
62 
66 final public class EventClusterNode extends EventBundleNodeBase<EventCluster, EventStripe, EventStripeNode> {
67 
68  private static final Logger LOGGER = Logger.getLogger(EventClusterNode.class.getName());
69 
70  private static final BorderWidths CLUSTER_BORDER_WIDTHS = new BorderWidths(2, 1, 2, 1);
71  private static final Image PLUS = new Image("/org/sleuthkit/autopsy/timeline/images/plus-button.png"); // NON-NLS //NOI18N
72  private static final Image MINUS = new Image("/org/sleuthkit/autopsy/timeline/images/minus-button.png"); // NON-NLS //NOI18N
73  private final Border clusterBorder = new Border(new BorderStroke(evtColor.deriveColor(0, 1, 1, .4), BorderStrokeStyle.SOLID, CORNER_RADII_1, CLUSTER_BORDER_WIDTHS));
74 
75  private Button plusButton;
76  private Button minusButton;
77 
78  @Override
79  void installActionButtons() {
80  if (plusButton == null) {
81  plusButton = ActionUtils.createButton(new ExpandClusterAction(), ActionUtils.ActionTextBehavior.HIDE);
82  minusButton = ActionUtils.createButton(new CollapseClusterAction(), ActionUtils.ActionTextBehavior.HIDE);
83 
84  configureLoDButton(plusButton);
85  configureLoDButton(minusButton);
86  infoHBox.getChildren().addAll(minusButton, plusButton);
87  }
88  }
89 
91  super(chart, eventCluster, parentNode);
92 
93  subNodePane.setBorder(clusterBorder);
94  subNodePane.setBackground(defaultBackground);
95  subNodePane.setMinWidth(1);
96  subNodePane.setMaxWidth(USE_PREF_SIZE);
97  setMinHeight(24);
98  setAlignment(Pos.CENTER_LEFT);
99 
100  setCursor(Cursor.HAND);
101 
102  getChildren().addAll(subNodePane, infoHBox);
103 
104  }
105 
106  @Override
107  void showHoverControls(final boolean showControls) {
108  super.showHoverControls(showControls);
109  installActionButtons();
110  show(plusButton, showControls);
111  show(minusButton, showControls);
112  }
113 
114  @Override
115  void applyHighlightEffect(boolean applied) {
116 // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
117  }
118 
119  @Override
120  void setMaxDescriptionWidth(double max) {
121 // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
122  }
123 
124  @Override
125  void setDescriptionVisibiltiyImpl(DescriptionVisibility descrVis) {
126  final int size = getEventCluster().getCount();
127  switch (descrVis) {
128  case HIDDEN:
129  countLabel.setText("");
130  descrLabel.setText("");
131  break;
132  case COUNT_ONLY:
133  descrLabel.setText("");
134  countLabel.setText(String.valueOf(size));
135  break;
136  default:
137  case SHOWN:
138  countLabel.setText(String.valueOf(size));
139  break;
140  }
141  }
142 
149  @NbBundle.Messages(value = "EventStripeNode.loggedTask.name=Load sub clusters")
150  @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
151  private synchronized void loadSubBundles(DescriptionLoD.RelativeDetail relativeDetail) {
152  chart.setCursor(Cursor.WAIT);
153 
154 
155  /*
156  * make new ZoomParams to query with
157  *
158  * We need to extend end time because for the query by one second,
159  * because it is treated as an open interval but we want to include
160  * events at exactly the time of the last event in this cluster
161  */
162  final RootFilter subClusterFilter = getSubClusterFilter();
163  final Interval subClusterSpan = new Interval(getStartMillis(), getEndMillis() + 1000);
164  final EventTypeZoomLevel eventTypeZoomLevel = eventsModel.eventTypeZoomProperty().get();
165  final ZoomParams zoomParams = new ZoomParams(subClusterSpan, eventTypeZoomLevel, subClusterFilter, getDescriptionLoD());
166 
167  Task<List<EventStripe>> loggedTask = new LoggedTask<List<EventStripe>>(Bundle.EventStripeNode_loggedTask_name(), false) {
168 
169  private volatile DescriptionLoD loadedDescriptionLoD = getDescriptionLoD().withRelativeDetail(relativeDetail);
170 
171  @Override
172  protected List<EventStripe> call() throws Exception {
173  List<EventStripe> bundles;
174  DescriptionLoD next = loadedDescriptionLoD;
175  do {
176  loadedDescriptionLoD = next;
177  if (loadedDescriptionLoD == getEventBundle().getDescriptionLoD()) {
178  return Collections.emptyList();
179  }
180  bundles = eventsModel.getEventStripes(zoomParams.withDescrLOD(loadedDescriptionLoD));
181 
182  next = loadedDescriptionLoD.withRelativeDetail(relativeDetail);
183  } while (bundles.size() == 1 && nonNull(next));
184 
185  // return list of EventStripes representing sub-bundles
186  return bundles.stream()
187  .map(eventStripe -> eventStripe.withParent(getEventCluster()))
188  .collect(Collectors.toList());
189  }
190 
191  @Override
192  protected void succeeded() {
193  try {
194  List<EventStripe> bundles = get();
195 
196  //clear the existing subnodes
197  List<EventStripe> transform = subNodes.stream().flatMap(new StripeFlattener()).collect(Collectors.toList());
198  chart.getEventStripes().removeAll(transform);
199  subNodes.clear();
200  if (bundles.isEmpty()) {
201  getChildren().setAll(subNodePane, infoHBox);
202  descLOD.set(getEventBundle().getDescriptionLoD());
203  } else {
204  chart.getEventStripes().addAll(bundles);
205  subNodes.addAll(Lists.transform(bundles, EventClusterNode.this::createChildNode));
206  getChildren().setAll(new VBox(infoHBox, subNodePane));
207  descLOD.set(loadedDescriptionLoD);
208  }
209  } catch (InterruptedException | ExecutionException ex) {
210  LOGGER.log(Level.SEVERE, "Error loading subnodes", ex); //NON-NLS
211  }
213  chart.setCursor(null);
214  }
215  };
216 
217  new Thread(loggedTask).start();
218  //start task
219  chart.getController().monitorTask(loggedTask);
220  }
221 
222  @Override
223  EventStripeNode createChildNode(EventStripe stripe) {
224  return new EventStripeNode(chart, stripe, this);
225  }
226 
227  EventCluster getEventCluster() {
228  return getEventBundle();
229  }
230 
231  @Override
232  protected void layoutChildren() {
233  double chartX = chart.getXAxis().getDisplayPosition(new DateTime(getStartMillis()));
234  double w = chart.getXAxis().getDisplayPosition(new DateTime(getEndMillis())) - chartX;
235  subNodePane.setPrefWidth(Math.max(1, w));
236  super.layoutChildren();
237  }
238 
244  RootFilter getSubClusterFilter() {
245  RootFilter subClusterFilter = eventsModel.filterProperty().get().copyOf();
246  subClusterFilter.getSubFilters().addAll(
247  new DescriptionFilter(getEventBundle().getDescriptionLoD(), getDescription(), DescriptionFilter.FilterMode.INCLUDE),
248  new TypeFilter(getEventType()));
249  return subClusterFilter;
250  }
251 
252  @Override
253  Collection<? extends Action> getActions() {
254  return Arrays.asList(new ExpandClusterAction(),
255  new CollapseClusterAction());
256  }
257 
258  @Override
259  EventHandler<MouseEvent> getDoubleClickHandler() {
260  return mouseEvent -> new ExpandClusterAction().handle(null);
261  }
262 
263  private class ExpandClusterAction extends Action {
264 
265  @NbBundle.Messages(value = "ExpandClusterAction.text=Expand")
267  super(Bundle.ExpandClusterAction_text());
268 
269  setGraphic(new ImageView(PLUS));
270  setEventHandler(actionEvent -> {
271  if (descLOD.get().moreDetailed() != null) {
273  }
274  });
275  disabledProperty().bind(descLOD.isEqualTo(DescriptionLoD.FULL));
276  }
277  }
278 
279  private class CollapseClusterAction extends Action {
280 
281  @NbBundle.Messages(value = "CollapseClusterAction.text=Collapse")
283  super(Bundle.CollapseClusterAction_text());
284 
285  setGraphic(new ImageView(MINUS));
286  setEventHandler(actionEvent -> {
287  if (descLOD.get().lessDetailed() != null) {
289  }
290  });
291  disabledProperty().bind(Bindings.createBooleanBinding(() -> nonNull(getEventCluster()) && descLOD.get() == getEventCluster().getDescriptionLoD(), descLOD));
292  }
293  }
294 
295 }
ZoomParams withDescrLOD(DescriptionLoD descrLOD)
Definition: ZoomParams.java:74
synchronized ReadOnlyObjectProperty< EventTypeZoomLevel > eventTypeZoomProperty()
synchronized void loadSubBundles(DescriptionLoD.RelativeDetail relativeDetail)
synchronized ReadOnlyObjectProperty< RootFilter > filterProperty()
synchronized void monitorTask(final Task<?> task)
EventClusterNode(EventDetailsChart chart, EventCluster eventCluster, EventStripeNode parentNode)
synchronized static Logger getLogger(String name)
Definition: Logger.java:166
DescriptionLoD withRelativeDetail(RelativeDetail relativeDetail)

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.