fix events tree; more cleanup

This commit is contained in:
jmillman 2016-03-10 16:18:11 -05:00
parent 5ab1fc3395
commit f949a00093
15 changed files with 162 additions and 172 deletions

View File

@ -22,7 +22,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Sets;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
@ -33,9 +32,9 @@ import org.sleuthkit.autopsy.timeline.utils.IntervalUtils;
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
/**
* Represents a set of other events clustered together. All the
* sub events should have the same type and matching descriptions at the
* designated 'zoom level', and be 'close together' in time.
* Represents a set of other events clustered together. All the sub events
* should have the same type and matching descriptions at the designated 'zoom
* level', and be 'close together' in time.
*/
@Immutable
public class EventCluster implements MultiEvent<EventStripe> {
@ -120,7 +119,7 @@ public class EventCluster implements MultiEvent<EventStripe> {
}
@Override
public Optional<EventStripe> getParentBundle() {
public Optional<EventStripe> getParent() {
return Optional.ofNullable(parent);
}
@ -181,9 +180,6 @@ public class EventCluster implements MultiEvent<EventStripe> {
* EventBundle as the parent.
*/
public EventCluster withParent(EventStripe parent) {
if (Objects.nonNull(this.parent)) {
throw new IllegalStateException("Event Cluster already has a parent!");
}
return new EventCluster(span, type, eventIDs, hashHits, tagged, description, lod, parent);
}
@ -192,5 +188,8 @@ public class EventCluster implements MultiEvent<EventStripe> {
return ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis)).add(this).build();
}
@Override
public String toString() {
return "EventCluster{" + "description=" + description + ", eventIDs=" + eventIDs.size() + '}';
}
}

View File

@ -22,10 +22,10 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
import java.util.SortedSet;
import javax.annotation.concurrent.Immutable;
import org.python.google.common.base.Objects;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
@ -39,10 +39,10 @@ public final class EventStripe implements MultiEvent<EventCluster> {
public static EventStripe merge(EventStripe u, EventStripe v) {
Preconditions.checkNotNull(u);
Preconditions.checkNotNull(v);
Preconditions.checkArgument(Objects.equal(u.description, v.description));
Preconditions.checkArgument(Objects.equal(u.lod, v.lod));
Preconditions.checkArgument(Objects.equal(u.type, v.type));
Preconditions.checkArgument(Objects.equal(u.parent, v.parent));
Preconditions.checkArgument(Objects.equals(u.description, v.description));
Preconditions.checkArgument(Objects.equals(u.lod, v.lod));
Preconditions.checkArgument(Objects.equals(u.type, v.type));
Preconditions.checkArgument(Objects.equals(u.parent, v.parent));
return new EventStripe(u, v);
}
@ -82,8 +82,10 @@ public final class EventStripe implements MultiEvent<EventCluster> {
private final ImmutableSet<Long> hashHits;
public EventStripe withParent(EventCluster parent) {
EventStripe eventStripe = new EventStripe(parent, this.type, this.description, this.lod, clusters, eventIDs, tagged, hashHits);
return eventStripe;
if (java.util.Objects.nonNull(this.parent)) {
throw new IllegalStateException("Event Stripe already has a parent!");
}
return new EventStripe(parent, this.type, this.description, this.lod, clusters, eventIDs, tagged, hashHits);
}
private EventStripe(EventCluster parent, EventType type, String description, DescriptionLoD lod, SortedSet<EventCluster> clusters, ImmutableSet<Long> eventIDs, ImmutableSet<Long> tagged, ImmutableSet<Long> hashHits) {
@ -98,9 +100,10 @@ public final class EventStripe implements MultiEvent<EventCluster> {
this.hashHits = hashHits;
}
public EventStripe(EventCluster cluster, EventCluster parent) {
public EventStripe(EventCluster cluster) {
this.clusters = ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis))
.add(cluster).build();
.add(cluster.withParent(this)).build();
type = cluster.getEventType();
description = cluster.getDescription();
@ -108,7 +111,7 @@ public final class EventStripe implements MultiEvent<EventCluster> {
eventIDs = cluster.getEventIDs();
tagged = cluster.getEventIDsWithTags();
hashHits = cluster.getEventIDsWithHashHits();
this.parent = parent;
this.parent = null;
}
private EventStripe(EventStripe u, EventStripe v) {
@ -132,14 +135,22 @@ public final class EventStripe implements MultiEvent<EventCluster> {
.addAll(u.getEventIDsWithHashHits())
.addAll(v.getEventIDsWithHashHits())
.build();
parent = u.getParentBundle().orElse(v.getParentBundle().orElse(null));
parent = u.getParent().orElse(v.getParent().orElse(null));
}
@Override
public Optional<EventCluster> getParentBundle() {
public Optional<EventCluster> getParent() {
return Optional.ofNullable(parent);
}
public Optional<EventStripe> getParentStripe() {
if (getParent().isPresent()) {
return getParent().get().getParent();
} else {
return Optional.empty();
}
}
@Override
public String getDescription() {
return description;
@ -191,8 +202,7 @@ public final class EventStripe implements MultiEvent<EventCluster> {
@Override
public String toString() {
return "EventStripe{" + "description=" + description + ", eventIDs=" + eventIDs.size() + '}'; //NON-NLS
return "EventStripe{" + "description=" + description + ", eventIDs=" + (Objects.isNull(eventIDs) ? 0 : eventIDs.size()) + '}'; //NON-NLS
}
}

View File

@ -45,7 +45,7 @@ public interface MultiEvent<ParentType extends MultiEvent<?>> extends TimeLineEv
long getStartMillis();
Optional<ParentType> getParentBundle();
Optional<ParentType> getParent();
default int getSize() {
return getEventIDs().size();

View File

@ -1050,7 +1050,7 @@ public class EventDB {
switch (Version.getBuildType()) {
case DEVELOPMENT:
LOGGER.log(Level.INFO, "executing timeline query: {0}", query); //NON-NLS
// LOGGER.log(Level.INFO, "executing timeline query: {0}", query); //NON-NLS
break;
case RELEASE:
default:
@ -1097,8 +1097,7 @@ public class EventDB {
Set<Long> hashHits = SQLHelper.unGroupConcat(rs.getString("hash_hits"), Long::valueOf); //NON-NLS
Set<Long> tagged = SQLHelper.unGroupConcat(rs.getString("taggeds"), Long::valueOf); //NON-NLS
return new EventCluster(interval, type, eventIDs, hashHits, tagged,
description, descriptionLOD);
return new EventCluster(interval, type, eventIDs, hashHits, tagged, description, descriptionLOD);
}
/**
@ -1159,7 +1158,7 @@ public class EventDB {
for (EventCluster eventCluster : aggEvents) {
stripeDescMap.merge(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription()),
new EventStripe(eventCluster, null), EventStripe::merge);
new EventStripe(eventCluster), EventStripe::merge);
}
return stripeDescMap.values().stream().sorted(Comparator.comparing(EventStripe::getStartMillis)).collect(Collectors.toList());

View File

@ -111,7 +111,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
}
public ObservableList<EventStripe> getEventStripes() {
return chart.getEventStripes();
return chart.getAllNestedEventStripes();
}
public void setSelectionModel(MultipleSelectionModel<TreeItem<TimeLineEvent>> selectionModel) {

View File

@ -67,6 +67,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 ObservableList<EventStripe> nestedEventStripes = FXCollections.observableArrayList();
DetailsChart(TimeLineController controller, DateAxis detailsChartDateAxis, DateAxis pinnedDateAxis, Axis<EventStripe> verticalAxis, ObservableList<EventNodeBase<?>> selectedNodes) {
this.controller = controller;
@ -99,6 +100,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);
}
void clearGuideLine(GuideLine guideLine) {
@ -120,6 +122,15 @@ public final class DetailsChart extends Control implements TimeLineChart<DateTim
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
void reset() {
eventStripes.clear();
nestedEventStripes.clear();
}
public ObservableList<EventStripe> getAllNestedEventStripes() {
return nestedEventStripes;
}
ObservableList<EventStripe> getEventStripes() {
return eventStripes;
}
private static class DetailIntervalSelector extends IntervalSelector<DateTime> {
@ -244,7 +255,7 @@ public final class DetailsChart extends Control implements TimeLineChart<DateTim
return new DetailsChartSkin(this);
}
ObservableList<EventStripe> getEventStripes() {
ObservableList<EventStripe> getRootEventStripes() {
return eventStripes;
}

View File

@ -25,6 +25,7 @@ import java.util.Collections;
import java.util.List;
import static java.util.Objects.nonNull;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javafx.concurrent.Task;
@ -119,12 +120,11 @@ final public class EventClusterNode extends MultiEventNodeBase<EventCluster, Eve
* @param requestedDescrLoD
* @param expand
*/
@NbBundle.Messages(value = "EventStripeNode.loggedTask.name=Load sub clusters")
@NbBundle.Messages(value = "EventClusterNode.loggedTask.name=Load sub events")
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private synchronized void loadSubBundles(DescriptionLoD.RelativeDetail relativeDetail) {
private synchronized void loadSubStripes(DescriptionLoD.RelativeDetail relativeDetail) {
getChartLane().setCursor(Cursor.WAIT);
/*
* make new ZoomParams to query with
*
@ -137,45 +137,50 @@ final public class EventClusterNode extends MultiEventNodeBase<EventCluster, Eve
final EventTypeZoomLevel eventTypeZoomLevel = eventsModel.eventTypeZoomProperty().get();
final ZoomParams zoomParams = new ZoomParams(subClusterSpan, eventTypeZoomLevel, subClusterFilter, getDescriptionLoD());
Task<List<EventStripe>> loggedTask = new LoggedTask<List<EventStripe>>(Bundle.EventStripeNode_loggedTask_name(), false) {
Task<List<EventStripe>> loggedTask = new LoggedTask<List<EventStripe>>(Bundle.EventClusterNode_loggedTask_name(), false) {
private volatile DescriptionLoD loadedDescriptionLoD = getDescriptionLoD().withRelativeDetail(relativeDetail);
@Override
protected List<EventStripe> call() throws Exception {
List<EventStripe> bundles;
List<EventStripe> stripes;
DescriptionLoD next = loadedDescriptionLoD;
do {
loadedDescriptionLoD = next;
if (loadedDescriptionLoD == getEventBundle().getDescriptionLoD()) {
if (loadedDescriptionLoD == getEvent().getDescriptionLoD()) {
return Collections.emptyList();
}
bundles = eventsModel.getEventStripes(zoomParams.withDescrLOD(loadedDescriptionLoD));
stripes = eventsModel.getEventStripes(zoomParams.withDescrLOD(loadedDescriptionLoD));
next = loadedDescriptionLoD.withRelativeDetail(relativeDetail);
} while (bundles.size() == 1 && nonNull(next));
} while (stripes.size() == 1 && nonNull(next));
// return list of EventStripes representing sub-bundles
return bundles.stream()
.map(eventStripe -> eventStripe.withParent(getEventCluster()))
return stripes.stream()
.map(new Function<EventStripe, EventStripe>() {
public EventStripe apply(EventStripe eventStripe) {
return eventStripe.withParent(getEvent());
}
})
.collect(Collectors.toList());
}
@Override
protected void succeeded() {
try {
List<EventStripe> bundles = get();
List<EventStripe> newSubStripes = get();
//clear the existing subnodes
List<TimeLineEvent> transform = subNodes.stream().flatMap(new StripeFlattener()).collect(Collectors.toList());
// getChartLane().getParentChart().getEventStripes().removeAll(transform);
List<TimeLineEvent> oldSubStripes = subNodes.stream().flatMap(new StripeFlattener()).collect(Collectors.toList());
getChartLane().getParentChart().getAllNestedEventStripes().removeAll(oldSubStripes);
subNodes.clear();
if (bundles.isEmpty()) {
if (newSubStripes.isEmpty()) {
getChildren().setAll(subNodePane, infoHBox);
setDescriptionLOD(getEventBundle().getDescriptionLoD());
setDescriptionLOD(getEvent().getDescriptionLoD());
} else {
// getChartLane().getParentChart().getEventStripes().addAll(bundles);
subNodes.addAll(Lists.transform(bundles, EventClusterNode.this::createChildNode));
getChartLane().getParentChart().getAllNestedEventStripes().addAll(newSubStripes);
subNodes.addAll(Lists.transform(newSubStripes, EventClusterNode.this::createChildNode));
getChildren().setAll(new VBox(infoHBox, subNodePane));
setDescriptionLOD(loadedDescriptionLoD);
}
@ -202,10 +207,6 @@ final public class EventClusterNode extends MultiEventNodeBase<EventCluster, Eve
}
}
EventCluster getEventCluster() {
return getEventBundle();
}
@Override
protected void layoutChildren() {
double chartX = getChartLane().getXAxis().getDisplayPosition(new DateTime(getStartMillis()));
@ -222,7 +223,7 @@ final public class EventClusterNode extends MultiEventNodeBase<EventCluster, Eve
RootFilter getSubClusterFilter() {
RootFilter subClusterFilter = eventsModel.filterProperty().get().copyOf();
subClusterFilter.getSubFilters().addAll(
new DescriptionFilter(getEventBundle().getDescriptionLoD(), getDescription(), DescriptionFilter.FilterMode.INCLUDE),
new DescriptionFilter(getEvent().getDescriptionLoD(), getDescription(), DescriptionFilter.FilterMode.INCLUDE),
new TypeFilter(getEventType()));
return subClusterFilter;
}
@ -251,7 +252,7 @@ final public class EventClusterNode extends MultiEventNodeBase<EventCluster, Eve
setGraphic(new ImageView(PLUS));
setEventHandler(actionEvent -> {
if (node.getDescriptionLoD().moreDetailed() != null) {
node.loadSubBundles(DescriptionLoD.RelativeDetail.MORE);
node.loadSubStripes(DescriptionLoD.RelativeDetail.MORE);
}
});
disabledProperty().bind(node.descriptionLoDProperty().isEqualTo(DescriptionLoD.FULL));
@ -269,11 +270,11 @@ final public class EventClusterNode extends MultiEventNodeBase<EventCluster, Eve
setGraphic(new ImageView(MINUS));
setEventHandler(actionEvent -> {
if (node.getDescriptionLoD().lessDetailed() != null) {
node.loadSubBundles(DescriptionLoD.RelativeDetail.LESS);
node.loadSubStripes(DescriptionLoD.RelativeDetail.LESS);
}
});
disabledProperty().bind(node.descriptionLoDProperty().isEqualTo(node.getEventCluster().getDescriptionLoD()));
disabledProperty().bind(node.descriptionLoDProperty().isEqualTo(node.getEvent().getDescriptionLoD()));
}
}
}

View File

@ -57,20 +57,19 @@ final public class EventStripeNode extends MultiEventNodeBase<EventStripe, Event
if (eventStripe.getClusters().size() > 1) {
for (EventCluster cluster : eventStripe.getClusters()) {
subNodes.add(createChildNode(cluster));
subNodes.add(createChildNode(cluster.withParent(eventStripe)));
}
getChildren().addAll(new VBox(infoHBox, subNodePane));
} else {
EventNodeBase<?> childNode;
EventCluster cluster = Iterables.getOnlyElement(eventStripe.getClusters());
EventCluster cluster = Iterables.getOnlyElement(eventStripe.getClusters()).withParent(eventStripe);
if (cluster.getEventIDs().size() == 1) {
SingleEventNode singleEventNode = new SingleEventNode(getChartLane(), getChartLane().getController().getEventsModel().getEventById(Iterables.getOnlyElement(cluster.getEventIDs())), this);
childNode = singleEventNode;
childNode = createChildNode(cluster);
} else {
EventClusterNode eventClusterNode = new EventClusterNode(getChartLane(), cluster, this);
EventClusterNode eventClusterNode = (EventClusterNode) createChildNode(cluster);
eventClusterNode.installActionButtons();
eventClusterNode.infoHBox.getChildren().remove(eventClusterNode.countLabel);
controlsHBox.getChildren().addAll(eventClusterNode.minusButton, eventClusterNode.plusButton);
eventClusterNode.infoHBox.getChildren().remove(eventClusterNode.countLabel);
childNode = eventClusterNode;
}
@ -81,7 +80,7 @@ final public class EventStripeNode extends MultiEventNodeBase<EventStripe, Event
}
public EventStripe getEventStripe() {
return getEventBundle();
return getEvent();
}
@Override

View File

@ -57,14 +57,14 @@ public abstract class MultiEventNodeBase< BundleType extends MultiEvent<ParentTy
private final ReadOnlyObjectWrapper<DescriptionLoD> descLOD = new ReadOnlyObjectWrapper<>();
MultiEventNodeBase(DetailsChartLane<?> chartLane, BundleType eventBundle, ParentNodeType parentNode) {
super(eventBundle, parentNode, chartLane);
setDescriptionLOD(eventBundle.getDescriptionLoD());
MultiEventNodeBase(DetailsChartLane<?> chartLane, BundleType event, ParentNodeType parentNode) {
super(event, parentNode, chartLane);
setDescriptionLOD(event.getDescriptionLoD());
if (eventBundle.getEventIDsWithHashHits().isEmpty()) {
if (event.getEventIDsWithHashHits().isEmpty()) {
show(hashIV, false);
}
if (eventBundle.getEventIDsWithTags().isEmpty()) {
if (event.getEventIDsWithTags().isEmpty()) {
show(tagIV, false);
}
@ -80,7 +80,7 @@ public abstract class MultiEventNodeBase< BundleType extends MultiEvent<ParentTy
*/
heightProperty().addListener(heightProp -> chartLane.requestLayout());
Platform.runLater(() ->
setLayoutX(chartLane.getXAxis().getDisplayPosition(new DateTime(eventBundle.getStartMillis())) - getLayoutXCompensation())
setLayoutX(chartLane.getXAxis().getDisplayPosition(new DateTime(event.getStartMillis())) - getLayoutXCompensation())
);
//initialize info hbox
@ -105,21 +105,17 @@ public abstract class MultiEventNodeBase< BundleType extends MultiEvent<ParentTy
descLOD.set(descriptionLoD);
}
public final BundleType getEventBundle() {
return getEvent();
}
@SuppressWarnings("unchecked")
public List<EventNodeBase<?>> getSubNodes() {
return subNodes;
}
final String getDescription() {
return getEventBundle().getDescription();
return getEvent().getDescription();
}
final Set<Long> getEventIDs() {
return getEventBundle().getEventIDs();
return getEvent().getEventIDs();
}
@Override

View File

@ -100,8 +100,6 @@ public final class PrimaryDetailsChartLane extends DetailsChartLane<EventStripe>
});
}
private double getParentXForEpochMillis(Long epochMillis) {
return getXAxis().localToParent(getXForEpochMillis(epochMillis), 0).getX();
}

View File

@ -24,57 +24,49 @@ import java.util.HashMap;
import java.util.Map;
import javafx.collections.FXCollections;
import javafx.scene.control.TreeItem;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.datamodel.MultiEvent;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
/**
*
*/
class EventDescriptionTreeItem extends NavTreeItem {
class EventDescriptionTreeItem extends EventsTreeItem {
/**
* maps a description to the child item of this item with that description
*/
private final Map<String, EventDescriptionTreeItem> childMap = new HashMap<>();
private final TimeLineEvent bundle;
private Comparator<TreeItem<TimeLineEvent>> comparator = TreeComparator.Description;
public TimeLineEvent getEvent() {
return bundle;
}
EventDescriptionTreeItem(TimeLineEvent g, Comparator<TreeItem<TimeLineEvent>> comp) {
bundle = g;
EventDescriptionTreeItem(EventStripe stripe, Comparator<TreeItem<TimeLineEvent>> comp) {
comparator = comp;
setValue(g);
}
@Override
public long getCount() {
return getValue().getSize();
setValue(stripe);
}
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
public void insert(Deque<MultiEvent<?>> path) {
MultiEvent<?> head = path.removeFirst();
EventDescriptionTreeItem treeItem = childMap.computeIfAbsent(head.getDescription(), description -> {
EventDescriptionTreeItem newTreeItem = new EventDescriptionTreeItem(head, comparator);
newTreeItem.setExpanded(true);
childMap.put(description, newTreeItem);
getChildren().add(newTreeItem);
resort(comparator, false);
return newTreeItem;
});
public void insert(Deque<EventStripe> path) {
EventStripe head = path.removeFirst();
String substringAfter = StringUtils.substringAfter(head.getDescription(), head.getParentStripe().map(EventStripe::getDescription).orElse(""));
EventDescriptionTreeItem treeItem = childMap.computeIfAbsent(substringAfter,
description -> {
EventDescriptionTreeItem newTreeItem = new EventDescriptionTreeItem(head, comparator);
newTreeItem.setExpanded(true);
getChildren().add(newTreeItem);
resort(comparator, false);
return newTreeItem;
});
if (path.isEmpty() == false) {
treeItem.insert(path);
}
}
void remove(Deque<MultiEvent<?>> path) {
MultiEvent<?> head = path.removeFirst();
EventDescriptionTreeItem descTreeItem = childMap.get(head.getDescription());
void remove(Deque<EventStripe> path) {
EventStripe 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);
}
@ -94,14 +86,14 @@ class EventDescriptionTreeItem extends NavTreeItem {
}
@Override
public NavTreeItem findTreeItemForEvent(TimeLineEvent t) {
public EventsTreeItem findTreeItemForEvent(TimeLineEvent event) {
if (getValue().getEventType() == t.getEventType()
&& getValue().getDescription().equals(t.getDescription())) {
if (getValue().getEventType() == event.getEventType()
&& getValue().getDescription().equals(event.getDescription())) {
return this;
} else {
for (EventDescriptionTreeItem child : childMap.values()) {
final NavTreeItem findTreeItemForEvent = child.findTreeItemForEvent(t);
final EventsTreeItem findTreeItemForEvent = child.findTreeItemForEvent(event);
if (findTreeItemForEvent != null) {
return findTreeItemForEvent;
}
@ -109,5 +101,4 @@ class EventDescriptionTreeItem extends NavTreeItem {
}
return null;
}
}

View File

@ -25,10 +25,10 @@ import java.util.Map;
import javafx.collections.FXCollections;
import javafx.scene.control.TreeItem;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.datamodel.MultiEvent;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
class EventTypeTreeItem extends NavTreeItem {
class EventTypeTreeItem extends EventsTreeItem {
/**
* maps a description to the child item of this item with that description
@ -37,35 +37,31 @@ class EventTypeTreeItem extends NavTreeItem {
private Comparator<TreeItem<TimeLineEvent>> comparator = TreeComparator.Description;
EventTypeTreeItem(MultiEvent<?> g, Comparator<TreeItem<TimeLineEvent>> comp) {
setValue(g);
EventTypeTreeItem(EventStripe stripe, Comparator<TreeItem<TimeLineEvent>> comp) {
setValue(stripe);
comparator = comp;
}
@Override
public long getCount() {
return getValue().getSize();
}
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
public void insert(Deque<MultiEvent<?>> path) {
MultiEvent<?> head = path.removeFirst();
EventDescriptionTreeItem treeItem = childMap.computeIfAbsent(head.getDescription(), description -> {
EventDescriptionTreeItem newTreeItem = new EventDescriptionTreeItem(head, comparator);
newTreeItem.setExpanded(true);
childMap.put(head.getDescription(), newTreeItem);
getChildren().add(newTreeItem);
resort(comparator, false);
return newTreeItem;
});
public void insert(Deque<EventStripe> path) {
EventStripe head = path.removeFirst();
EventDescriptionTreeItem treeItem = childMap.computeIfAbsent(head.getDescription(),
description -> {
EventDescriptionTreeItem newTreeItem = new EventDescriptionTreeItem(head, comparator);
newTreeItem.setExpanded(true);
getChildren().add(newTreeItem);
resort(comparator, false);
return newTreeItem;
});
if (path.isEmpty() == false) {
treeItem.insert(path);
}
}
void remove(Deque<MultiEvent<?>> path) {
MultiEvent<?> head = path.removeFirst();
void remove(Deque<EventStripe> path) {
EventStripe head = path.removeFirst();
EventDescriptionTreeItem descTreeItem = childMap.get(head.getDescription());
if (descTreeItem != null) {
if (path.isEmpty() == false) {
@ -79,11 +75,11 @@ class EventTypeTreeItem extends NavTreeItem {
}
@Override
public NavTreeItem findTreeItemForEvent(TimeLineEvent t) {
public EventsTreeItem findTreeItemForEvent(TimeLineEvent t) {
if (t.getEventType().getBaseType() == getValue().getEventType().getBaseType()) {
for (EventDescriptionTreeItem child : childMap.values()) {
final NavTreeItem findTreeItemForEvent = child.findTreeItemForEvent(t);
final EventsTreeItem findTreeItemForEvent = child.findTreeItemForEvent(t);
if (findTreeItemForEvent != null) {
return findTreeItemForEvent;
}

View File

@ -49,6 +49,7 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.FXMLConstructor;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.MultiEvent;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.filters.AbstractFilter;
@ -86,15 +87,11 @@ final public class EventsTree extends BorderPane {
this.detailViewPane = detailViewPane;
detailViewPane.setSelectionModel(eventsTree.getSelectionModel());
detailViewPane.getEventStripes().addListener((ListChangeListener.Change<? extends MultiEvent<?>> c) -> {
detailViewPane.getEventStripes().addListener((ListChangeListener.Change<? extends EventStripe> c) -> {
//on jfx thread
while (c.next()) {
for (MultiEvent<?> bundle : c.getAddedSubList()) {
getRoot().insert(bundle);
}
for (MultiEvent<?> bundle : c.getRemoved()) {
getRoot().remove(bundle);
}
c.getRemoved().forEach(getRoot()::remove);
c.getAddedSubList().forEach(getRoot()::insert);
}
});
@ -116,9 +113,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()));
for (MultiEvent<?> bundle : detailViewPane.getEventStripes()) {
root.insert(bundle);
}
detailViewPane.getEventStripes().forEach(root::insert);
eventsTree.setRoot(root);
}

View File

@ -28,12 +28,14 @@ import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
* {@link EventTreeCell}. Each NavTreeItem has a EventBundle which has a type,
* description , count, etc.
*/
abstract class NavTreeItem extends TreeItem<TimeLineEvent> {
abstract class EventsTreeItem extends TreeItem<TimeLineEvent> {
abstract long getCount();
final long getSize() {
return getValue().getSize();
}
abstract void resort(Comparator<TreeItem<TimeLineEvent>> comp, Boolean recursive);
abstract NavTreeItem findTreeItemForEvent(TimeLineEvent t);
abstract EventsTreeItem findTreeItemForEvent(TimeLineEvent event);
}

View File

@ -26,14 +26,14 @@ import java.util.Map;
import java.util.Optional;
import javafx.scene.control.TreeItem;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.datamodel.MultiEvent;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
/**
*
*/
class RootItem extends NavTreeItem {
class RootItem extends EventsTreeItem {
/**
* maps a description to the child item of this item with that description
@ -49,52 +49,45 @@ class RootItem extends NavTreeItem {
this.comparator = comp;
}
@Override
public long getCount() {
return getValue().getSize();
}
/**
* Recursive method to add a grouping at a given path.
*
* @param bundle bundle to add
* @param stripe stripe to add
*/
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
public void insert(MultiEvent<?> bundle) {
public void insert(EventStripe stripe) {
EventTypeTreeItem treeItem = childMap.computeIfAbsent(bundle.getEventType().getBaseType(),
EventTypeTreeItem treeItem = childMap.computeIfAbsent(stripe.getEventType().getBaseType(),
baseType -> {
EventTypeTreeItem newTreeItem = new EventTypeTreeItem(bundle, comparator);
EventTypeTreeItem newTreeItem = new EventTypeTreeItem(stripe, comparator);
newTreeItem.setExpanded(true);
getChildren().add(newTreeItem);
return newTreeItem;
});
treeItem.insert(getTreePath(bundle));
treeItem.insert(getTreePath(stripe));
}
void remove(MultiEvent<?> bundle) {
EventTypeTreeItem typeTreeItem = childMap.get(bundle.getEventType().getBaseType());
void remove(EventStripe stripe) {
EventTypeTreeItem typeTreeItem = childMap.get(stripe.getEventType().getBaseType());
if (typeTreeItem != null) {
typeTreeItem.remove(getTreePath(bundle));
typeTreeItem.remove(getTreePath(stripe));
if (typeTreeItem.getChildren().isEmpty()) {
childMap.remove(bundle.getEventType().getBaseType());
childMap.remove(stripe.getEventType().getBaseType());
getChildren().remove(typeTreeItem);
}
}
}
static Deque< MultiEvent<?>> getTreePath(MultiEvent<?> g) {
Deque<MultiEvent<?>> path = new ArrayDeque<>();
Optional<? extends MultiEvent<?>> p = Optional.of(g);
while (p.isPresent()) {
MultiEvent<?> parent = p.get();
static Deque< EventStripe> getTreePath(EventStripe event) {
Deque<EventStripe> path = new ArrayDeque<>();
path.addFirst(event);
Optional<EventStripe> parentOptional = event.getParentStripe();
while (parentOptional.isPresent()) {
EventStripe parent = parentOptional.get();
path.addFirst(parent);
p = parent.getParentBundle();
parentOptional = parent.getParentStripe();
}
return path;
}
@ -105,9 +98,9 @@ class RootItem extends NavTreeItem {
}
@Override
public NavTreeItem findTreeItemForEvent(TimeLineEvent t) {
public EventsTreeItem findTreeItemForEvent(TimeLineEvent t) {
for (EventTypeTreeItem child : childMap.values()) {
final NavTreeItem findTreeItemForEvent = child.findTreeItemForEvent(t);
final EventsTreeItem findTreeItemForEvent = child.findTreeItemForEvent(t);
if (findTreeItemForEvent != null) {
return findTreeItemForEvent;
}