fix another bug in EventsTree and make it more general (takes TimeLineEvent instead of EventStripe)

This commit is contained in:
jmillman 2016-04-13 16:43:54 -04:00
parent 2f087fb022
commit 395f248a06
14 changed files with 122 additions and 57 deletions

View File

@ -132,6 +132,11 @@ public class EventCluster implements MultiEvent<EventStripe> {
return Optional.ofNullable(parent);
}
@Override
public Optional<EventStripe> getParentStripe() {
return getParent();
}
public Interval getSpan() {
return span;
}

View File

@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import javax.annotation.Nullable;
@ -50,10 +51,12 @@ public class SingleEvent implements TimeLineEvent {
private final boolean hashHit;
private final boolean tagged;
private MultiEvent<?> parent = null;
public SingleEvent(long eventID, long dataSourceID, long objID, @Nullable Long artifactID, long time, EventType type, String fullDescription, String medDescription, String shortDescription, TskData.FileKnown known, boolean hashHit, boolean tagged) {
this.eventID = eventID;
this.fileID = objID;
this.artifactID = artifactID == 0 ? null : artifactID;
this.artifactID = (artifactID == null || artifactID == 0) ? null : artifactID;
this.time = time;
this.subType = type;
descriptions = ImmutableMap.<DescriptionLoD, String>of(DescriptionLoD.FULL, fullDescription,
@ -66,6 +69,12 @@ public class SingleEvent implements TimeLineEvent {
this.dataSourceID = dataSourceID;
}
public SingleEvent withParent(MultiEvent<?> newParent) {
SingleEvent singleEvent = new SingleEvent(eventID, dataSourceID, fileID, artifactID, time, subType, descriptions.get(DescriptionLoD.FULL), descriptions.get(DescriptionLoD.MEDIUM), descriptions.get(DescriptionLoD.SHORT), known, hashHit, tagged);
singleEvent.parent = newParent;
return singleEvent;
}
public boolean isTagged() {
return tagged;
}
@ -94,6 +103,7 @@ public class SingleEvent implements TimeLineEvent {
return time;
}
@Override
public EventType getEventType() {
return subType;
}
@ -184,4 +194,15 @@ public class SingleEvent implements TimeLineEvent {
public DescriptionLoD getDescriptionLoD() {
return DescriptionLoD.FULL;
}
@Override
public Optional<EventStripe> getParentStripe() {
if (parent == null) {
return Optional.empty();
} else if (parent instanceof EventStripe) {
return Optional.of((EventStripe) parent);
} else {
return parent.getParentStripe();
}
}
}

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.timeline.datamodel;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
@ -32,6 +33,8 @@ public interface TimeLineEvent {
public DescriptionLoD getDescriptionLoD();
public Optional<EventStripe> getParentStripe();
Set<Long> getEventIDs();
Set<Long> getEventIDsWithHashHits();

View File

@ -114,8 +114,11 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
});
}
public ObservableList<EventStripe> getAllEventStripes() {
return chart.getAllNestedEventStripes();
/*
* gets the tree of event stripes flattened into a list
*/
public ObservableList<TimeLineEvent> getAllNestedEvents() {
return chart.getAllNestedEvents();
}
public ObservableList<TimeLineEvent> getSelectedEvents() {

View File

@ -66,7 +66,7 @@ public final class DetailsChart extends Control implements TimeLineChart<DateTim
private final ObservableList<EventNodeBase<?>> selectedNodes;
private final DetailsChartLayoutSettings layoutSettings = new DetailsChartLayoutSettings();
private final TimeLineController controller;
private final ObservableList<EventStripe> nestedEventStripes = FXCollections.observableArrayList();
private final ObservableList<TimeLineEvent> nestedEvents = FXCollections.observableArrayList();
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private final ObservableList<EventStripe> eventStripes = FXCollections.observableArrayList();
@ -103,7 +103,7 @@ public final class DetailsChart extends Control implements TimeLineChart<DateTim
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
void addStripe(EventStripe stripe) {
eventStripes.add(stripe);
nestedEventStripes.add(stripe);
nestedEvents.add(stripe);
}
void clearGuideLines() {
@ -129,11 +129,14 @@ public final class DetailsChart extends Control implements TimeLineChart<DateTim
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
void reset() {
eventStripes.clear();
nestedEventStripes.clear();
nestedEvents.clear();
}
public ObservableList<EventStripe> getAllNestedEventStripes() {
return nestedEventStripes;
/*
* gets the tree of event stripes flattened into a list
*/
public ObservableList<TimeLineEvent> getAllNestedEvents() {
return nestedEvents;
}
private static class DetailIntervalSelector extends IntervalSelector<DateTime> {
@ -279,7 +282,7 @@ public final class DetailsChart extends Control implements TimeLineChart<DateTim
DescriptionFilter descriptionFilter = chart.getController().getQuickHideFilters().stream()
.filter(testFilter::equals)
.findFirst().orElseGet(() -> {
testFilter.selectedProperty().addListener(observable -> chart.requestLayout());
testFilter.selectedProperty().addListener(observable -> chart.requestLayout());
chart.getController().getQuickHideFilters().add(testFilter);
return testFilter;
});

View File

@ -166,15 +166,16 @@ final public class EventClusterNode extends MultiEventNodeBase<EventCluster, Eve
List<EventStripe> newSubStripes = get();
//clear the existing subnodes
List<TimeLineEvent> oldSubStripes = subNodes.stream().flatMap(new StripeFlattener()).collect(Collectors.toList());
getChartLane().getParentChart().getAllNestedEventStripes().removeAll(oldSubStripes);
List<TimeLineEvent> oldSubEvents = subNodes.stream().flatMap(new StripeFlattener()).collect(Collectors.toList());
getChartLane().getParentChart().getAllNestedEvents().removeAll(oldSubEvents);
subNodes.clear();
if (newSubStripes.isEmpty()) {
getChildren().setAll(subNodePane, infoHBox);
setDescriptionLOD(getEvent().getDescriptionLoD());
} else {
getChartLane().getParentChart().getAllNestedEventStripes().addAll(newSubStripes);
subNodes.addAll(Lists.transform(newSubStripes, EventClusterNode.this::createChildNode));
List<TimeLineEvent> newSubEvents = subNodes.stream().flatMap(new StripeFlattener()).collect(Collectors.toList());
getChartLane().getParentChart().getAllNestedEvents().addAll(newSubEvents);
getChildren().setAll(new VBox(infoHBox, subNodePane));
setDescriptionLOD(loadedDescriptionLoD);
}
@ -195,7 +196,7 @@ final public class EventClusterNode extends MultiEventNodeBase<EventCluster, Eve
@Override
EventNodeBase<?> createChildNode(EventStripe stripe) {
if (stripe.getEventIDs().size() == 1) {
return new SingleEventNode(getChartLane(), getChartLane().getController().getEventsModel().getEventById(Iterables.getOnlyElement(stripe.getEventIDs())), this);
return new SingleEventNode(getChartLane(), getChartLane().getController().getEventsModel().getEventById(Iterables.getOnlyElement(stripe.getEventIDs())).withParent(stripe), this);
} else {
return new EventStripeNode(getChartLane(), stripe, this);
}

View File

@ -98,7 +98,7 @@ final public class EventStripeNode extends MultiEventNodeBase<EventStripe, Event
@Override
EventNodeBase<?> createChildNode(EventCluster cluster) {
if (cluster.getEventIDs().size() == 1) {
return new SingleEventNode(getChartLane(), getChartLane().getController().getEventsModel().getEventById(Iterables.getOnlyElement(cluster.getEventIDs())), this);
return new SingleEventNode(getChartLane(), getChartLane().getController().getEventsModel().getEventById(Iterables.getOnlyElement(cluster.getEventIDs())).withParent(cluster), this);
} else {
return new EventClusterNode(getChartLane(), cluster, this);
}

View File

@ -23,7 +23,6 @@ import java.util.Deque;
import java.util.function.Supplier;
import javafx.scene.control.TreeItem;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
@ -32,14 +31,14 @@ import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
*/
class BaseTypeTreeItem extends EventTypeTreeItem<EventsTreeItem> {
BaseTypeTreeItem(EventStripe stripe, Comparator<TreeItem<TimeLineEvent>> comp) {
BaseTypeTreeItem(TimeLineEvent stripe, Comparator<TreeItem<TimeLineEvent>> comp) {
super(stripe.getEventType().getBaseType(), comp);
}
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
@Override
public void insert(Deque<EventStripe> path) {
EventStripe peek = path.peek();
public void insert(Deque<TimeLineEvent> path) {
TimeLineEvent peek = path.peek();
Supplier< EventsTreeItem> treeItemConstructor;
String descriptionKey;
/*
@ -51,7 +50,7 @@ class BaseTypeTreeItem extends EventTypeTreeItem<EventsTreeItem> {
treeItemConstructor = () -> configureNewTreeItem(new SubTypeTreeItem(peek, getComparator()));
} else {
descriptionKey = peek.getDescription();
EventStripe stripe = path.removeFirst();
TimeLineEvent stripe = path.removeFirst();
treeItemConstructor = () -> configureNewTreeItem(new EventDescriptionTreeItem(stripe, getComparator()));
}
@ -64,9 +63,9 @@ class BaseTypeTreeItem extends EventTypeTreeItem<EventsTreeItem> {
}
@Override
void remove(Deque<EventStripe> path) {
void remove(Deque<TimeLineEvent> path) {
EventStripe head = path.peek();
TimeLineEvent head = path.peek();
EventsTreeItem descTreeItem;
if (head.getEventType().getZoomLevel() == EventTypeZoomLevel.SUB_TYPE) {

View File

@ -40,14 +40,14 @@ class EventDescriptionTreeItem extends EventsTreeItem {
*/
private final Map<String, EventDescriptionTreeItem> childMap = new HashMap<>();
EventDescriptionTreeItem(EventStripe stripe, Comparator<TreeItem<TimeLineEvent>> comp) {
EventDescriptionTreeItem(TimeLineEvent stripe, Comparator<TreeItem<TimeLineEvent>> comp) {
super(comp);
setValue(stripe);
}
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
public void insert(Deque<EventStripe> path) {
EventStripe head = path.removeFirst();
public void insert(Deque<TimeLineEvent> path) {
TimeLineEvent head = path.removeFirst();
String substringAfter = StringUtils.substringAfter(head.getDescription(), head.getParentStripe().map(EventStripe::getDescription).orElse(""));
EventDescriptionTreeItem treeItem = childMap.computeIfAbsent(substringAfter,
description -> configureNewTreeItem(new EventDescriptionTreeItem(head, getComparator()))
@ -59,16 +59,19 @@ class EventDescriptionTreeItem extends EventsTreeItem {
}
@Override
void remove(Deque<EventStripe> path) {
EventStripe head = path.removeFirst();
void remove(Deque<TimeLineEvent> path) {
TimeLineEvent head = path.removeFirst();
String substringAfter = StringUtils.substringAfter(head.getDescription(), head.getParentStripe().map(EventStripe::getDescription).orElse(""));
EventDescriptionTreeItem descTreeItem = childMap.get(substringAfter);
if (path.isEmpty() == false) {
descTreeItem.remove(path);
}
if (descTreeItem.getChildren().isEmpty()) {
childMap.remove(head.getDescription());
getChildren().remove(descTreeItem);
if (descTreeItem != null) {
if (path.isEmpty() == false) {
descTreeItem.remove(path);
}
if (descTreeItem.getChildren().isEmpty()) {
childMap.remove(substringAfter);
getChildren().remove(descTreeItem);
}
}
}

View File

@ -1,7 +1,20 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* Autopsy Forensic Browser
*
* Copyright 2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.ui.detailview.tree;
@ -13,7 +26,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
/**
*
* EventTreeItem for event types
*/
abstract class EventTypeTreeItem<T extends EventsTreeItem> extends EventsTreeItem {

View File

@ -86,7 +86,7 @@ final public class EventsTree extends BorderPane {
public void setDetailViewPane(DetailViewPane detailViewPane) {
this.detailViewPane = detailViewPane;
detailViewPane.getAllEventStripes().addListener((ListChangeListener.Change<? extends EventStripe> c) -> {
detailViewPane.getAllNestedEvents().addListener((ListChangeListener.Change<? extends TimeLineEvent> c) -> {
//on jfx thread
while (c.next()) {
c.getRemoved().forEach(getRoot()::remove);
@ -112,7 +112,7 @@ final public class EventsTree extends BorderPane {
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private void setRoot() {
RootItem root = new RootItem(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()));
detailViewPane.getAllEventStripes().forEach(root::insert);
detailViewPane.getAllNestedEvents().forEach(root::insert);
eventsTree.setRoot(root);
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Copyright 2014-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.timeline.ui.detailview.tree;
import java.util.Comparator;
import java.util.Deque;
import javafx.scene.control.TreeItem;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
@ -58,9 +57,9 @@ abstract class EventsTreeItem extends TreeItem<TimeLineEvent> {
abstract EventType getEventType();
abstract void remove(Deque<EventStripe> path);
abstract void remove(Deque<TimeLineEvent> path);
abstract void insert(Deque<EventStripe> path);
abstract void insert(Deque<TimeLineEvent> path);
<T extends EventsTreeItem> T configureNewTreeItem(T newTreeItem) {
newTreeItem.setExpanded(true);

View File

@ -50,7 +50,7 @@ class RootItem extends EventsTreeItem {
* @param stripe stripe to add
*/
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
public void insert(EventStripe stripe) {
public void insert(TimeLineEvent stripe) {
BaseTypeTreeItem treeItem = childMap.computeIfAbsent(stripe.getEventType().getBaseType(),
baseType -> configureNewTreeItem(new BaseTypeTreeItem(stripe, getComparator()))
@ -58,7 +58,7 @@ class RootItem extends EventsTreeItem {
treeItem.insert(getTreePath(stripe));
}
void remove(EventStripe stripe) {
void remove(TimeLineEvent stripe) {
BaseTypeTreeItem typeTreeItem = childMap.get(stripe.getEventType().getBaseType());
if (typeTreeItem != null) {
typeTreeItem.remove(getTreePath(stripe));
@ -70,8 +70,8 @@ class RootItem extends EventsTreeItem {
}
}
static Deque< EventStripe> getTreePath(EventStripe event) {
Deque<EventStripe> path = new ArrayDeque<>();
static Deque< TimeLineEvent> getTreePath(TimeLineEvent event) {
Deque<TimeLineEvent> path = new ArrayDeque<>();
path.addFirst(event);
Optional<EventStripe> parentOptional = event.getParentStripe();
while (parentOptional.isPresent()) {
@ -110,12 +110,12 @@ class RootItem extends EventsTreeItem {
}
@Override
void remove(Deque<EventStripe> path) {
void remove(Deque<TimeLineEvent> path) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
void insert(Deque<EventStripe> path) {
void insert(Deque<TimeLineEvent> path) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}

View File

@ -1,7 +1,20 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* Autopsy Forensic Browser
*
* Copyright 2013-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.ui.detailview.tree;
@ -9,18 +22,20 @@ import java.util.Comparator;
import java.util.Deque;
import javafx.scene.control.TreeItem;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
/**
* EventTreeItem for sub event types
*/
public class SubTypeTreeItem extends EventTypeTreeItem<EventDescriptionTreeItem> {
SubTypeTreeItem(EventStripe stripe, Comparator<TreeItem<TimeLineEvent>> comp) {
SubTypeTreeItem(TimeLineEvent stripe, Comparator<TreeItem<TimeLineEvent>> comp) {
super(stripe.getEventType(), comp);
}
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
public void insert(Deque<EventStripe> path) {
EventStripe head = path.removeFirst();
public void insert(Deque<TimeLineEvent> path) {
TimeLineEvent head = path.removeFirst();
EventDescriptionTreeItem treeItem = childMap.computeIfAbsent(head.getDescription(),
description -> configureNewTreeItem(new EventDescriptionTreeItem(head, getComparator()))
);
@ -31,8 +46,8 @@ public class SubTypeTreeItem extends EventTypeTreeItem<EventDescriptionTreeItem>
}
@Override
void remove(Deque<EventStripe> path) {
EventStripe head = path.removeFirst();
void remove(Deque<TimeLineEvent> path) {
TimeLineEvent head = path.removeFirst();
EventsTreeItem descTreeItem = childMap.get(head.getDescription());
if (descTreeItem != null) {
if (path.isEmpty() == false) {