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.ImmutableSortedSet;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import java.util.Comparator; import java.util.Comparator;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
@ -33,9 +32,9 @@ import org.sleuthkit.autopsy.timeline.utils.IntervalUtils;
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
/** /**
* Represents a set of other events clustered together. All the * Represents a set of other events clustered together. All the sub events
* sub events should have the same type and matching descriptions at the * should have the same type and matching descriptions at the designated 'zoom
* designated 'zoom level', and be 'close together' in time. * level', and be 'close together' in time.
*/ */
@Immutable @Immutable
public class EventCluster implements MultiEvent<EventStripe> { public class EventCluster implements MultiEvent<EventStripe> {
@ -120,7 +119,7 @@ public class EventCluster implements MultiEvent<EventStripe> {
} }
@Override @Override
public Optional<EventStripe> getParentBundle() { public Optional<EventStripe> getParent() {
return Optional.ofNullable(parent); return Optional.ofNullable(parent);
} }
@ -181,9 +180,6 @@ public class EventCluster implements MultiEvent<EventStripe> {
* EventBundle as the parent. * EventBundle as the parent.
*/ */
public EventCluster withParent(EventStripe 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); 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(); 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.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.ImmutableSortedSet;
import java.util.Comparator; import java.util.Comparator;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.SortedSet; import java.util.SortedSet;
import javax.annotation.concurrent.Immutable; 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.datamodel.eventtype.EventType;
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; 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) { public static EventStripe merge(EventStripe u, EventStripe v) {
Preconditions.checkNotNull(u); Preconditions.checkNotNull(u);
Preconditions.checkNotNull(v); Preconditions.checkNotNull(v);
Preconditions.checkArgument(Objects.equal(u.description, v.description)); Preconditions.checkArgument(Objects.equals(u.description, v.description));
Preconditions.checkArgument(Objects.equal(u.lod, v.lod)); Preconditions.checkArgument(Objects.equals(u.lod, v.lod));
Preconditions.checkArgument(Objects.equal(u.type, v.type)); Preconditions.checkArgument(Objects.equals(u.type, v.type));
Preconditions.checkArgument(Objects.equal(u.parent, v.parent)); Preconditions.checkArgument(Objects.equals(u.parent, v.parent));
return new EventStripe(u, v); return new EventStripe(u, v);
} }
@ -82,8 +82,10 @@ public final class EventStripe implements MultiEvent<EventCluster> {
private final ImmutableSet<Long> hashHits; private final ImmutableSet<Long> hashHits;
public EventStripe withParent(EventCluster parent) { public EventStripe withParent(EventCluster parent) {
EventStripe eventStripe = new EventStripe(parent, this.type, this.description, this.lod, clusters, eventIDs, tagged, hashHits); if (java.util.Objects.nonNull(this.parent)) {
return eventStripe; 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) { 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; this.hashHits = hashHits;
} }
public EventStripe(EventCluster cluster, EventCluster parent) { public EventStripe(EventCluster cluster) {
this.clusters = ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis)) this.clusters = ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis))
.add(cluster).build(); .add(cluster.withParent(this)).build();
type = cluster.getEventType(); type = cluster.getEventType();
description = cluster.getDescription(); description = cluster.getDescription();
@ -108,7 +111,7 @@ public final class EventStripe implements MultiEvent<EventCluster> {
eventIDs = cluster.getEventIDs(); eventIDs = cluster.getEventIDs();
tagged = cluster.getEventIDsWithTags(); tagged = cluster.getEventIDsWithTags();
hashHits = cluster.getEventIDsWithHashHits(); hashHits = cluster.getEventIDsWithHashHits();
this.parent = parent; this.parent = null;
} }
private EventStripe(EventStripe u, EventStripe v) { private EventStripe(EventStripe u, EventStripe v) {
@ -132,14 +135,22 @@ public final class EventStripe implements MultiEvent<EventCluster> {
.addAll(u.getEventIDsWithHashHits()) .addAll(u.getEventIDsWithHashHits())
.addAll(v.getEventIDsWithHashHits()) .addAll(v.getEventIDsWithHashHits())
.build(); .build();
parent = u.getParentBundle().orElse(v.getParentBundle().orElse(null)); parent = u.getParent().orElse(v.getParent().orElse(null));
} }
@Override @Override
public Optional<EventCluster> getParentBundle() { public Optional<EventCluster> getParent() {
return Optional.ofNullable(parent); return Optional.ofNullable(parent);
} }
public Optional<EventStripe> getParentStripe() {
if (getParent().isPresent()) {
return getParent().get().getParent();
} else {
return Optional.empty();
}
}
@Override @Override
public String getDescription() { public String getDescription() {
return description; return description;
@ -191,8 +202,7 @@ public final class EventStripe implements MultiEvent<EventCluster> {
@Override @Override
public String toString() { 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(); long getStartMillis();
Optional<ParentType> getParentBundle(); Optional<ParentType> getParent();
default int getSize() { default int getSize() {
return getEventIDs().size(); return getEventIDs().size();

View File

@ -1050,7 +1050,7 @@ public class EventDB {
switch (Version.getBuildType()) { switch (Version.getBuildType()) {
case DEVELOPMENT: 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; break;
case RELEASE: case RELEASE:
default: default:
@ -1097,8 +1097,7 @@ public class EventDB {
Set<Long> hashHits = SQLHelper.unGroupConcat(rs.getString("hash_hits"), Long::valueOf); //NON-NLS 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 Set<Long> tagged = SQLHelper.unGroupConcat(rs.getString("taggeds"), Long::valueOf); //NON-NLS
return new EventCluster(interval, type, eventIDs, hashHits, tagged, return new EventCluster(interval, type, eventIDs, hashHits, tagged, description, descriptionLOD);
description, descriptionLOD);
} }
/** /**
@ -1159,7 +1158,7 @@ public class EventDB {
for (EventCluster eventCluster : aggEvents) { for (EventCluster eventCluster : aggEvents) {
stripeDescMap.merge(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription()), 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()); 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() { public ObservableList<EventStripe> getEventStripes() {
return chart.getEventStripes(); return chart.getAllNestedEventStripes();
} }
public void setSelectionModel(MultipleSelectionModel<TreeItem<TimeLineEvent>> selectionModel) { 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 ObservableList<EventNodeBase<?>> selectedNodes;
private final DetailsChartLayoutSettings layoutSettings = new DetailsChartLayoutSettings(); private final DetailsChartLayoutSettings layoutSettings = new DetailsChartLayoutSettings();
private final TimeLineController controller; private final TimeLineController controller;
private ObservableList<EventStripe> nestedEventStripes = FXCollections.observableArrayList();
DetailsChart(TimeLineController controller, DateAxis detailsChartDateAxis, DateAxis pinnedDateAxis, Axis<EventStripe> verticalAxis, ObservableList<EventNodeBase<?>> selectedNodes) { DetailsChart(TimeLineController controller, DateAxis detailsChartDateAxis, DateAxis pinnedDateAxis, Axis<EventStripe> verticalAxis, ObservableList<EventNodeBase<?>> selectedNodes) {
this.controller = controller; this.controller = controller;
@ -99,6 +100,7 @@ public final class DetailsChart extends Control implements TimeLineChart<DateTim
@ThreadConfined(type = ThreadConfined.ThreadType.JFX) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
void addStripe(EventStripe stripe) { void addStripe(EventStripe stripe) {
eventStripes.add(stripe); eventStripes.add(stripe);
nestedEventStripes.add(stripe);
} }
void clearGuideLine(GuideLine guideLine) { void clearGuideLine(GuideLine guideLine) {
@ -120,6 +122,15 @@ public final class DetailsChart extends Control implements TimeLineChart<DateTim
@ThreadConfined(type = ThreadConfined.ThreadType.JFX) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
void reset() { void reset() {
eventStripes.clear(); eventStripes.clear();
nestedEventStripes.clear();
}
public ObservableList<EventStripe> getAllNestedEventStripes() {
return nestedEventStripes;
}
ObservableList<EventStripe> getEventStripes() {
return eventStripes;
} }
private static class DetailIntervalSelector extends IntervalSelector<DateTime> { private static class DetailIntervalSelector extends IntervalSelector<DateTime> {
@ -244,7 +255,7 @@ public final class DetailsChart extends Control implements TimeLineChart<DateTim
return new DetailsChartSkin(this); return new DetailsChartSkin(this);
} }
ObservableList<EventStripe> getEventStripes() { ObservableList<EventStripe> getRootEventStripes() {
return eventStripes; return eventStripes;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -49,6 +49,7 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.FXMLConstructor;
import org.sleuthkit.autopsy.timeline.TimeLineController; 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.MultiEvent;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.filters.AbstractFilter; import org.sleuthkit.autopsy.timeline.filters.AbstractFilter;
@ -86,15 +87,11 @@ final public class EventsTree extends BorderPane {
this.detailViewPane = detailViewPane; this.detailViewPane = detailViewPane;
detailViewPane.setSelectionModel(eventsTree.getSelectionModel()); detailViewPane.setSelectionModel(eventsTree.getSelectionModel());
detailViewPane.getEventStripes().addListener((ListChangeListener.Change<? extends MultiEvent<?>> c) -> { detailViewPane.getEventStripes().addListener((ListChangeListener.Change<? extends EventStripe> c) -> {
//on jfx thread //on jfx thread
while (c.next()) { while (c.next()) {
for (MultiEvent<?> bundle : c.getAddedSubList()) { c.getRemoved().forEach(getRoot()::remove);
getRoot().insert(bundle); c.getAddedSubList().forEach(getRoot()::insert);
}
for (MultiEvent<?> bundle : c.getRemoved()) {
getRoot().remove(bundle);
}
} }
}); });
@ -116,9 +113,7 @@ final public class EventsTree extends BorderPane {
@ThreadConfined(type = ThreadConfined.ThreadType.JFX) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private void setRoot() { private void setRoot() {
RootItem root = new RootItem(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem())); RootItem root = new RootItem(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()));
for (MultiEvent<?> bundle : detailViewPane.getEventStripes()) { detailViewPane.getEventStripes().forEach(root::insert);
root.insert(bundle);
}
eventsTree.setRoot(root); 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, * {@link EventTreeCell}. Each NavTreeItem has a EventBundle which has a type,
* description , count, etc. * 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 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 java.util.Optional;
import javafx.scene.control.TreeItem; import javafx.scene.control.TreeItem;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; 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.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; 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 * maps a description to the child item of this item with that description
@ -49,52 +49,45 @@ class RootItem extends NavTreeItem {
this.comparator = comp; this.comparator = comp;
} }
@Override
public long getCount() {
return getValue().getSize();
}
/** /**
* Recursive method to add a grouping at a given path. * 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) @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 -> { baseType -> {
EventTypeTreeItem newTreeItem = new EventTypeTreeItem(bundle, comparator); EventTypeTreeItem newTreeItem = new EventTypeTreeItem(stripe, comparator);
newTreeItem.setExpanded(true); newTreeItem.setExpanded(true);
getChildren().add(newTreeItem); getChildren().add(newTreeItem);
return newTreeItem; return newTreeItem;
}); });
treeItem.insert(getTreePath(bundle)); treeItem.insert(getTreePath(stripe));
} }
void remove(MultiEvent<?> bundle) { void remove(EventStripe stripe) {
EventTypeTreeItem typeTreeItem = childMap.get(bundle.getEventType().getBaseType()); EventTypeTreeItem typeTreeItem = childMap.get(stripe.getEventType().getBaseType());
if (typeTreeItem != null) { if (typeTreeItem != null) {
typeTreeItem.remove(getTreePath(bundle)); typeTreeItem.remove(getTreePath(stripe));
if (typeTreeItem.getChildren().isEmpty()) { if (typeTreeItem.getChildren().isEmpty()) {
childMap.remove(bundle.getEventType().getBaseType()); childMap.remove(stripe.getEventType().getBaseType());
getChildren().remove(typeTreeItem); getChildren().remove(typeTreeItem);
} }
} }
} }
static Deque< MultiEvent<?>> getTreePath(MultiEvent<?> g) { static Deque< EventStripe> getTreePath(EventStripe event) {
Deque<MultiEvent<?>> path = new ArrayDeque<>(); Deque<EventStripe> path = new ArrayDeque<>();
Optional<? extends MultiEvent<?>> p = Optional.of(g); path.addFirst(event);
Optional<EventStripe> parentOptional = event.getParentStripe();
while (p.isPresent()) { while (parentOptional.isPresent()) {
MultiEvent<?> parent = p.get(); EventStripe parent = parentOptional.get();
path.addFirst(parent); path.addFirst(parent);
p = parent.getParentBundle(); parentOptional = parent.getParentStripe();
} }
return path; return path;
} }
@ -105,9 +98,9 @@ class RootItem extends NavTreeItem {
} }
@Override @Override
public NavTreeItem findTreeItemForEvent(TimeLineEvent t) { public EventsTreeItem findTreeItemForEvent(TimeLineEvent t) {
for (EventTypeTreeItem child : childMap.values()) { for (EventTypeTreeItem child : childMap.values()) {
final NavTreeItem findTreeItemForEvent = child.findTreeItemForEvent(t); final EventsTreeItem findTreeItemForEvent = child.findTreeItemForEvent(t);
if (findTreeItemForEvent != null) { if (findTreeItemForEvent != null) {
return findTreeItemForEvent; return findTreeItemForEvent;
} }