From 7d3354150bb2db9e6b742db68d8ee2cbfb39aba9 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 30 Oct 2015 13:40:05 -0400 Subject: [PATCH 1/8] experimental events tree optimizations --- .../tree/EventDescriptionTreeItem.java | 14 ++++- .../ui/detailview/tree/EventTypeTreeItem.java | 19 ++++++- .../ui/detailview/tree/EventsTree.java | 57 +++++++++++++------ .../ui/detailview/tree/NavTreeItem.java | 2 + .../timeline/ui/detailview/tree/RootItem.java | 24 ++++++-- 5 files changed, 90 insertions(+), 26 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventDescriptionTreeItem.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventDescriptionTreeItem.java index 6feef28f8f..0d172a73ba 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventDescriptionTreeItem.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventDescriptionTreeItem.java @@ -59,7 +59,6 @@ class EventDescriptionTreeItem extends NavTreeItem { treeItem.setExpanded(true); childMap.put(head.getDescription(), treeItem); getChildren().add(treeItem); - FXCollections.sort(getChildren(), TreeComparator.Description); } if (path.isEmpty() == false) { @@ -67,6 +66,18 @@ class EventDescriptionTreeItem extends NavTreeItem { } } + void remove(Deque> path) { + EventBundle head = path.removeFirst(); + EventDescriptionTreeItem descTreeItem = childMap.get(head.getDescription()); + if (path.isEmpty() == false) { + descTreeItem.remove(path); + } + if (descTreeItem.getChildren().isEmpty()) { + childMap.remove(head.getDescription()); + getChildren().remove(descTreeItem); + } + } + @Override public void resort(Comparator>> comp) { FXCollections.sort(getChildren(), comp); @@ -88,4 +99,5 @@ class EventDescriptionTreeItem extends NavTreeItem { } return null; } + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventTypeTreeItem.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventTypeTreeItem.java index e921991191..7d63fda0c2 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventTypeTreeItem.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventTypeTreeItem.java @@ -52,7 +52,6 @@ class EventTypeTreeItem extends NavTreeItem { treeItem.setExpanded(true); childMap.put(head.getDescription(), treeItem); getChildren().add(treeItem); - FXCollections.sort(getChildren(), comparator); } if (path.isEmpty() == false) { @@ -60,6 +59,21 @@ class EventTypeTreeItem extends NavTreeItem { } } + void remove(Deque> path) { + + EventBundle head = path.removeFirst(); + EventDescriptionTreeItem descTreeItem = childMap.get(head.getDescription()); + if (descTreeItem != null) { + if (path.isEmpty() == false) { + descTreeItem.remove(path); + } else if (descTreeItem.getChildren().isEmpty()) { + childMap.remove(head.getDescription()); + getChildren().remove(descTreeItem); + + } + } + } + @Override public NavTreeItem findTreeItemForEvent(EventBundle t) { if (t.getEventType().getBaseType() == getValue().getEventType().getBaseType()) { @@ -75,7 +89,8 @@ class EventTypeTreeItem extends NavTreeItem { } @Override - public void resort(Comparator>> comp) { + public void resort(Comparator>> comp + ) { FXCollections.sort(getChildren(), comp); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventsTree.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventsTree.java index 154ab9fab0..d212e49a5f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventsTree.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventsTree.java @@ -25,10 +25,10 @@ import java.util.Comparator; import java.util.Objects; import javafx.beans.InvalidationListener; import javafx.beans.Observable; +import javafx.beans.property.SimpleBooleanProperty; import javafx.collections.ListChangeListener; import javafx.fxml.FXML; import javafx.scene.control.ComboBox; -import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.SelectionMode; import javafx.scene.control.Tooltip; @@ -36,6 +36,8 @@ import javafx.scene.control.TreeCell; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.image.ImageView; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; @@ -61,6 +63,7 @@ final public class EventsTree extends BorderPane { private final TimeLineController controller; + private DetailViewPane detailViewPane; @FXML @@ -74,7 +77,7 @@ final public class EventsTree extends BorderPane { public EventsTree(TimeLineController controller) { this.controller = controller; - + FXMLConstructor.construct(this, "EventsTree.fxml"); // NON-NLS } @@ -82,9 +85,18 @@ final public class EventsTree extends BorderPane { this.detailViewPane = detailViewPane; detailViewPane.setSelectionModel(eventsTree.getSelectionModel()); - detailViewPane.getEventBundles().addListener((Observable observable) -> { - setRoot(); + detailViewPane.getEventBundles().addListener((ListChangeListener.Change> c) -> { + while (c.next()) { + for (EventBundle bundle : c.getAddedSubList()) { + getRoot().insert(bundle); + } + for (EventBundle bundle : c.getRemoved()) { + getRoot().remove(bundle); + } + } + getRoot().resort(sortByBox.getSelectionModel().getSelectedItem()); }); + setRoot(); detailViewPane.getSelectedNodes().addListener((Observable observable) -> { @@ -96,8 +108,8 @@ final public class EventsTree extends BorderPane { } - private NavTreeItem getRoot() { - return (NavTreeItem) eventsTree.getRoot(); + private RootItem getRoot() { + return (RootItem) eventsTree.getRoot(); } @ThreadConfined(type = ThreadConfined.ThreadType.JFX) @@ -106,6 +118,7 @@ final public class EventsTree extends BorderPane { for (EventBundle bundle : detailViewPane.getEventBundles()) { root.insert(bundle); } + root.resort(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem())); eventsTree.setRoot(root); } @@ -118,7 +131,7 @@ final public class EventsTree extends BorderPane { sortByBox.getItems().setAll(Arrays.asList(TreeComparator.Description, TreeComparator.Count)); sortByBox.getSelectionModel().select(TreeComparator.Description); sortByBox.getSelectionModel().selectedItemProperty().addListener((Observable o) -> { - getRoot().resort(sortByBox.getSelectionModel().getSelectedItem()); + getRoot().resort(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem())); }); eventsTree.setShowRoot(false); eventsTree.setCellFactory((TreeView> p) -> new EventBundleTreeCell()); @@ -137,6 +150,7 @@ final public class EventsTree extends BorderPane { private final Rectangle rect = new Rectangle(24, 24); private final ImageView imageView = new ImageView(); private InvalidationListener filterStateChangeListener; + SimpleBooleanProperty hidden = new SimpleBooleanProperty(false); EventBundleTreeCell() { rect.setArcHeight(5); @@ -173,6 +187,21 @@ final public class EventsTree extends BorderPane { imageView.setImage(item.getEventType().getFXImage()); setGraphic(new StackPane(rect, imageView)); updateHiddenState(item); + if (getTreeItem() instanceof EventDescriptionTreeItem) { + setOnMouseClicked((MouseEvent event) -> { + if (event.getButton() == MouseButton.SECONDARY) { + if (hidden.get()) { + ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newUnhideDescriptionAction(item.getDescription(), item.getDescriptionLoD()))) + .show(EventBundleTreeCell.this, event.getScreenX(), event.getScreenY()); + } else { + ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newHideDescriptionAction(item.getDescription(), item.getDescriptionLoD()))) + .show(EventBundleTreeCell.this, event.getScreenX(), event.getScreenY()); + } + } + }); + } else { + setOnMouseClicked(null); + } } } @@ -194,10 +223,11 @@ final public class EventsTree extends BorderPane { private void updateHiddenState(EventBundle item) { TreeItem> treeItem = getTreeItem(); - ContextMenu newMenu; - if (controller.getQuickHideFilters().stream(). + + hidden.set(controller.getQuickHideFilters().stream(). filter(AbstractFilter::isActive) - .anyMatch(filter -> filter.getDescription().equals(item.getDescription()))) { + .anyMatch(filter -> filter.getDescription().equals(item.getDescription()))); + if (hidden.get()) { if (treeItem != null) { treeItem.setExpanded(false); } @@ -205,18 +235,11 @@ final public class EventsTree extends BorderPane { imageView.setOpacity(HIDDEN_MULTIPLIER); rect.setStroke(item.getEventType().getColor().deriveColor(0, HIDDEN_MULTIPLIER, 1, HIDDEN_MULTIPLIER)); rect.setFill(item.getEventType().getColor().deriveColor(0, HIDDEN_MULTIPLIER, HIDDEN_MULTIPLIER, 0.1)); - newMenu = ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newUnhideDescriptionAction(item.getDescription(), item.getDescriptionLoD()))); } else { setTextFill(Color.BLACK); imageView.setOpacity(1); rect.setStroke(item.getEventType().getColor()); rect.setFill(item.getEventType().getColor().deriveColor(0, 1, 1, 0.1)); - newMenu = ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newHideDescriptionAction(item.getDescription(), item.getDescriptionLoD()))); - } - if (treeItem instanceof EventDescriptionTreeItem) { - setContextMenu(newMenu); - } else { - setContextMenu(null); } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/NavTreeItem.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/NavTreeItem.java index bed99d2270..30d1fdd9ea 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/NavTreeItem.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/NavTreeItem.java @@ -35,4 +35,6 @@ abstract class NavTreeItem extends TreeItem> { abstract void resort(Comparator>> comp); abstract NavTreeItem findTreeItemForEvent(EventBundle t); + + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/RootItem.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/RootItem.java index 571758a037..b59b11aa55 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/RootItem.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/RootItem.java @@ -54,19 +54,30 @@ class RootItem extends NavTreeItem { /** * Recursive method to add a grouping at a given path. * - * @param g Group to add + * @param bundle bundle to add */ - public void insert(EventBundle g) { + public void insert(EventBundle bundle) { - EventTypeTreeItem treeItem = childMap.computeIfAbsent(g.getEventType().getBaseType(), + EventTypeTreeItem treeItem = childMap.computeIfAbsent(bundle.getEventType().getBaseType(), baseType -> { - EventTypeTreeItem newTreeItem = new EventTypeTreeItem(g); + EventTypeTreeItem newTreeItem = new EventTypeTreeItem(bundle); newTreeItem.setExpanded(true); getChildren().add(newTreeItem); - getChildren().sort(TreeComparator.Type); return newTreeItem; }); - treeItem.insert(getTreePath(g)); + treeItem.insert(getTreePath(bundle)); + } + + void remove(EventBundle bundle) { + EventTypeTreeItem typeTreeItem = childMap.get(bundle.getEventType().getBaseType()); + if (typeTreeItem != null) { + typeTreeItem.remove(getTreePath(bundle)); + + if (typeTreeItem.getChildren().isEmpty()) { + childMap.remove(bundle.getEventType().getBaseType()); + getChildren().remove(typeTreeItem); + } + } } static Deque< EventBundle> getTreePath(EventBundle g) { @@ -97,4 +108,5 @@ class RootItem extends NavTreeItem { } return null; } + } From ff2278a98eabc46aeabde82be2d2163fa7117e7a Mon Sep 17 00:00:00 2001 From: jmillman Date: Mon, 2 Nov 2015 17:20:33 -0500 Subject: [PATCH 2/8] set correct text on treecell for event type nodes --- .../ui/detailview/tree/EventsTree.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventsTree.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventsTree.java index d212e49a5f..fe8bd81568 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventsTree.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventsTree.java @@ -63,7 +63,6 @@ final public class EventsTree extends BorderPane { private final TimeLineController controller; - private DetailViewPane detailViewPane; @FXML @@ -77,7 +76,7 @@ final public class EventsTree extends BorderPane { public EventsTree(TimeLineController controller) { this.controller = controller; - + FXMLConstructor.construct(this, "EventsTree.fxml"); // NON-NLS } @@ -96,7 +95,7 @@ final public class EventsTree extends BorderPane { } getRoot().resort(sortByBox.getSelectionModel().getSelectedItem()); }); - + setRoot(); detailViewPane.getSelectedNodes().addListener((Observable observable) -> { @@ -177,10 +176,15 @@ final public class EventsTree extends BorderPane { updateHiddenState(item); }); registerListeners(controller.getQuickHideFilters(), item); - String text = item.getDescription() + " (" + item.getCount() + ")"; // NON-NLS - TreeItem> parent = getTreeItem().getParent(); - if (parent != null && parent.getValue() != null && (parent instanceof EventDescriptionTreeItem)) { - text = StringUtils.substringAfter(text, parent.getValue().getDescription()); + String text; + if (getTreeItem() instanceof EventTypeTreeItem) { + text = item.getEventType().getDisplayName(); + } else { + text = item.getDescription() + " (" + item.getCount() + ")"; // NON-NLS + TreeItem> parent = getTreeItem().getParent(); + if (parent != null && parent.getValue() != null && (parent instanceof EventDescriptionTreeItem)) { + text = StringUtils.substringAfter(text, parent.getValue().getDescription()); + } } setText(text); setTooltip(new Tooltip(text)); From ddcc2d567f73eaec436156851831036c0eb2aa4d Mon Sep 17 00:00:00 2001 From: jmillman Date: Mon, 9 Nov 2015 16:44:57 -0500 Subject: [PATCH 3/8] put off invoking the JFX thread till deeper in the stack. remove unused list, use concurrent hashmaps --- .../ui/AbstractVisualizationPane.java | 3 +- .../ui/countsview/CountsViewPane.java | 11 ++-- .../ui/detailview/DetailViewPane.java | 17 +++--- .../ui/detailview/EventDetailsChart.java | 53 ++++++++++--------- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java index 52d64c0c1f..719269bbcd 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java @@ -33,7 +33,6 @@ import javafx.concurrent.Task; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.chart.Axis; -import javafx.scene.chart.BarChart; import javafx.scene.chart.Chart; import javafx.scene.chart.XYChart; import javafx.scene.control.Label; @@ -81,7 +80,7 @@ public abstract class AbstractVisualizationPane } protected final SimpleBooleanProperty hasEvents = new SimpleBooleanProperty(true); - protected final ObservableList> dataSets = FXCollections.>observableArrayList(); + protected final ObservableList> dataSeries = FXCollections.> observableArrayList(); protected C chart; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java index ff5a3d4798..80e46601d9 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java @@ -111,7 +111,7 @@ public class CountsViewPane extends AbstractVisualizationPane series.getData().stream()) + return dataSeries.stream().flatMap((series) -> series.getData().stream()) .anyMatch((data) -> data.getXValue().equals(value) && data.getYValue().intValue() > 0); } @@ -144,7 +144,7 @@ public class CountsViewPane extends AbstractVisualizationPane { updateMessage(NbBundle.getMessage(this.getClass(), "CountsViewPane.loggedTask.resetUI")); eventTypeMap.clear(); - dataSets.clear(); + dataSeries.clear(); dateAxis.getCategories().clear(); DateTime start = timeRange.getStart(); @@ -264,7 +264,7 @@ public class CountsViewPane extends AbstractVisualizationPane(); series.setName(et.getDisplayName()); eventTypeMap.put(et, series); - - dataSets.add(series); + dataSeries.add(series); } return series; @@ -395,7 +394,7 @@ public class CountsViewPane extends AbstractVisualizationPane s : dataSets) { + for (XYChart.Series s : dataSeries) { s.getData().forEach((XYChart.Data d) -> { if (startDateString.contains(d.getXValue())) { selectedNodes.add(d.getNode()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java index 6a8e6355f2..cf6a11dae2 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java @@ -111,15 +111,13 @@ public class DetailViewPane extends AbstractVisualizationPane(new DetailViewSettingsPane().getChildrenUnmodifiable()); - //bind layout fo axes and spacers dateAxis.setTickLabelGap(0); dateAxis.setAutoRanging(false); @@ -260,7 +258,10 @@ public class DetailViewPane extends AbstractVisualizationPane { XYChart.Series series = new XYChart.Series<>(); series.setName(et.getDisplayName()); - dataSets.add(series); + Platform.runLater(() -> { + dataSeries.add(series); + }); + return series; }); } @@ -299,7 +300,7 @@ public class DetailViewPane extends AbstractVisualizationPane eventClusters = filteredEvents.getEventClusters(); @@ -315,9 +316,9 @@ public class DetailViewPane extends AbstractVisualizationPane xyData = new BarChart.Data<>(new DateTime(cluster.getSpan().getStartMillis()), cluster); if (isCancelled() == false) { - Platform.runLater(() -> { - getSeries(cluster.getEventType()).getData().add(xyData); - }); +// Platform.runLater(() -> { + getSeries(cluster.getEventType()).getData().add(xyData); +// }); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java index 906b1c5511..c7522e1e6f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java @@ -24,11 +24,11 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.MissingResourceException; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -36,6 +36,7 @@ import java.util.stream.Stream; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; +import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.property.ReadOnlyDoubleProperty; @@ -148,17 +149,9 @@ public final class EventDetailsChart extends XYChart imp */ private final Group nodeGroup = new Group(); private final ObservableList> bundles = FXCollections.observableArrayList(); - private final Map, EventStripe> stripeDescMap = new HashMap<>(); - private final Map stripeNodeMap = new HashMap<>(); - private final Map projectionMap = new HashMap<>(); - - /** - * list of series of data added to this chart - * - * TODO: replace this with a map from name to series? -jm - */ - private final ObservableList> seriesList = - FXCollections.>observableArrayList(); + private final Map, EventStripe> stripeDescMap = new ConcurrentHashMap<>(); + private final Map stripeNodeMap = new ConcurrentHashMap<>(); + private final Map projectionMap = new ConcurrentHashMap<>(); /** * true == layout each event type in its own band, false == mix all the @@ -334,21 +327,26 @@ public final class EventDetailsChart extends XYChart imp @Override protected synchronized void dataItemAdded(Series series, int i, Data data) { final EventCluster eventCluster = data.getYValue(); - bundles.add(eventCluster); + EventStripe eventStripe = stripeDescMap.merge(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription()), new EventStripe(eventCluster, null), (EventStripe u, EventStripe v) -> { - EventStripeNode remove = stripeNodeMap.remove(u); - nodeGroup.getChildren().remove(remove); - remove = stripeNodeMap.remove(v); - nodeGroup.getChildren().remove(remove); + EventStripeNode removeU = stripeNodeMap.remove(u); + EventStripeNode removeV = stripeNodeMap.remove(v); + Platform.runLater(() -> { + nodeGroup.getChildren().remove(removeU); + nodeGroup.getChildren().remove(removeV); + }); return EventStripe.merge(u, v); } ); EventStripeNode stripeNode = new EventStripeNode(EventDetailsChart.this, eventStripe, null); stripeNodeMap.put(eventStripe, stripeNode); - nodeGroup.getChildren().add(stripeNode); - data.setNode(stripeNode); + Platform.runLater(() -> { + bundles.add(eventCluster); + nodeGroup.getChildren().add(stripeNode); + data.setNode(stripeNode); + }); } @Override @@ -360,11 +358,18 @@ public final class EventDetailsChart extends XYChart imp @Override protected synchronized void dataItemRemoved(Data data, Series series) { EventCluster eventCluster = data.getYValue(); - bundles.removeAll(eventCluster); + Platform.runLater(() -> { + bundles.removeAll(eventCluster); + }); + EventStripe removedStripe = stripeDescMap.remove(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription())); - EventStripeNode removedNode = stripeNodeMap.remove(removedStripe); - nodeGroup.getChildren().remove(removedNode); - data.setNode(null); + if (removedStripe != null) { + EventStripeNode removedNode = stripeNodeMap.remove(removedStripe); + Platform.runLater(() -> { + nodeGroup.getChildren().remove(removedNode); + data.setNode(null); + }); + } } @Override @@ -396,7 +401,6 @@ public final class EventDetailsChart extends XYChart imp for (int j = 0; j < series.getData().size(); j++) { dataItemAdded(series, j, series.getData().get(j)); } - seriesList.add(series); } @Override @@ -404,7 +408,6 @@ public final class EventDetailsChart extends XYChart imp for (int j = 0; j < series.getData().size(); j++) { dataItemRemoved(series.getData().get(j), series); } - seriesList.remove(series); } ReadOnlyDoubleProperty maxVScrollProperty() { From aed99b647f194ad8bacecf2266333758c941b59a Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 12 Nov 2015 14:38:04 -0500 Subject: [PATCH 4/8] more optimization WIP --- .../ui/AbstractVisualizationPane.java | 7 +- .../ui/countsview/CountsViewPane.java | 20 ++++-- .../ui/detailview/DetailViewPane.java | 68 ++++++++++--------- .../ui/detailview/EventBundleNodeBase.java | 20 ++++++ .../ui/detailview/EventDetailsChart.java | 33 ++++----- 5 files changed, 86 insertions(+), 62 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java index 719269bbcd..e870b064f7 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.timeline.ui; import com.google.common.eventbus.Subscribe; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.logging.Level; @@ -52,6 +53,7 @@ import javax.annotation.concurrent.Immutable; import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; @@ -273,9 +275,8 @@ public abstract class AbstractVisualizationPane * * _________october___________|_____________september___________ * - * - * NOTE: This method should only be invoked on the JFX thread */ + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) public synchronized void layoutDateLabels() { //clear old labels @@ -284,7 +285,7 @@ public abstract class AbstractVisualizationPane //since the tickmarks aren't necessarily in value/position order, //make a clone of the list sorted by position along axis ObservableList> tickMarks = FXCollections.observableArrayList(getXAxis().getTickMarks()); - tickMarks.sort((Axis.TickMark t, Axis.TickMark t1) -> Double.compare(t.getPosition(), t1.getPosition())); + tickMarks.sort(Comparator.comparing(Axis.TickMark::getPosition)); if (tickMarks.isEmpty() == false) { //get the spacing between ticks in the underlying axis diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java index 80e46601d9..e72b0403db 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java @@ -61,6 +61,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.ColorUtilities; import org.sleuthkit.autopsy.coreutils.LoggedTask; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.VisualizationMode; @@ -331,15 +332,20 @@ public class CountsViewPane extends AbstractVisualizationPane getSeries(final EventType et) { - XYChart.Series series = eventTypeMap.get(et); - if (series == null) { - series = new XYChart.Series<>(); + return eventTypeMap.computeIfAbsent(et, (EventType t) -> { + XYChart.Series series = new XYChart.Series<>(); series.setName(et.getDisplayName()); - eventTypeMap.put(et, series); - dataSeries.add(series); - } - return series; + dataSeries.add(series); + return series; + }); + +// XYChart.Series series = eventTypeMap.get(et); +// if (series == null) { +// +// } +// return series; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java index cf6a11dae2..5517a0d415 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java @@ -31,7 +31,6 @@ import javafx.collections.ObservableList; import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.geometry.Orientation; -import javafx.scene.Cursor; import javafx.scene.chart.Axis; import javafx.scene.chart.BarChart; import javafx.scene.chart.XYChart; @@ -64,7 +63,6 @@ import org.joda.time.DateTime; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.LoggedTask; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.EventBundle; @@ -113,6 +111,7 @@ public class DetailViewPane extends AbstractVisualizationPane getSeries(final EventType et) { return eventTypeToSeriesMap.computeIfAbsent(et, (EventType t) -> { XYChart.Series series = new XYChart.Series<>(); series.setName(et.getDisplayName()); - Platform.runLater(() -> { - dataSeries.add(series); - }); +// Platform.runLater(() -> { + dataSeries.add(series); +// }); return series; }); @@ -276,12 +275,18 @@ public class DetailViewPane extends AbstractVisualizationPane { - setCursor(Cursor.WAIT); - }); - } +// else { +// Platform.runLater(new Runnable() { +// +// public void run() { +// ProgressIndicator progressIndicator = new ProgressIndicator(-1); +// progressIndicator.progressProperty().bind(progressProperty()); +// progressIndicator.setOpacity(.5); +// setCenter(new StackPane(chart, progressIndicator)); +// setCursor(Cursor.WAIT); +// } +// }); +// } updateProgress(-1, 1); updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.preparing")); @@ -291,44 +296,45 @@ public class DetailViewPane extends AbstractVisualizationPane { - if (isCancelled()) { - return; + public void run() { + if (isCancelled()) { + return; + } + dateAxis.setLowerBound(new DateTime(lowerBound, TimeLineController.getJodaTimeZone())); + dateAxis.setUpperBound(new DateTime(upperBound, TimeLineController.getJodaTimeZone())); + vertScrollBar.setValue(0); + eventTypeToSeriesMap.clear(); } - dateAxis.setLowerBound(new DateTime(lowerBound, TimeLineController.getJodaTimeZone())); - dateAxis.setUpperBound(new DateTime(upperBound, TimeLineController.getJodaTimeZone())); - vertScrollBar.setValue(0); - eventTypeToSeriesMap.clear(); - dataSeries.clear(); }); List eventClusters = filteredEvents.getEventClusters(); final int size = eventClusters.size(); + updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.updateUI")); for (int i = 0; i < size; i++) { if (isCancelled()) { break; } final EventCluster cluster = eventClusters.get(i); updateProgress(i, size); - updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.updateUI")); - final XYChart.Data xyData = new BarChart.Data<>(new DateTime(cluster.getSpan().getStartMillis()), cluster); + final XYChart.Data xyData = new BarChart.Data<>(new DateTime(cluster.getStartMillis()), cluster); - if (isCancelled() == false) { // Platform.runLater(() -> { - getSeries(cluster.getEventType()).getData().add(xyData); + getSeries(cluster.getEventType()).getData().add(xyData); // }); - } } - - Platform.runLater(() -> { - setCursor(Cursor.DEFAULT); - layoutDateLabels(); - updateProgress(1, 1); - }); + updateProgress(1, 1); return eventClusters.isEmpty() == false; } + + @Override + protected void succeeded() { + super.succeeded(); + layoutDateLabels(); + } }; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventBundleNodeBase.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventBundleNodeBase.java index de3e33abce..0163c0388c 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventBundleNodeBase.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventBundleNodeBase.java @@ -27,9 +27,13 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.stream.Collectors; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.animation.Timeline; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; import javafx.concurrent.Task; +import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.geometry.Pos; @@ -55,6 +59,7 @@ import static javafx.scene.layout.Region.USE_COMPUTED_SIZE; import static javafx.scene.layout.Region.USE_PREF_SIZE; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; +import javafx.util.Duration; import org.joda.time.DateTime; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; @@ -121,6 +126,7 @@ public abstract class EventBundleNodeBase { + chart.requestChartLayout(); + }); + timeline.play(); + } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java index c7522e1e6f..3ce076c544 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java @@ -33,9 +33,6 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; -import javafx.animation.KeyFrame; -import javafx.animation.KeyValue; -import javafx.animation.Timeline; import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.Observable; @@ -62,7 +59,6 @@ import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; import javafx.scene.shape.Line; import javafx.scene.shape.StrokeLineCap; -import javafx.util.Duration; import org.apache.commons.lang3.tuple.ImmutablePair; import org.controlsfx.control.action.Action; import org.controlsfx.control.action.ActionUtils; @@ -187,6 +183,7 @@ public final class EventDetailsChart extends XYChart imp EventDetailsChart(TimeLineController controller, DateAxis dateAxis, final Axis verticalAxis, ObservableList> selectedNodes) { super(dateAxis, verticalAxis); + this.controller = controller; this.filteredEvents = this.controller.getEventsModel(); @@ -259,7 +256,6 @@ public final class EventDetailsChart extends XYChart imp } chartContextMenu = ActionUtils.createContextMenu(Arrays.asList(new PlaceMarkerAction(clickEvent), - // new StartIntervalSelectionAction(clickEvent, dragHandler), TimeLineChart.newZoomHistoyActionGroup(controller))); chartContextMenu.setAutoHide(true); return chartContextMenu; @@ -350,13 +346,13 @@ public final class EventDetailsChart extends XYChart imp } @Override - protected synchronized void dataItemChanged(Data data) { + protected void dataItemChanged(Data data) { //TODO: can we use this to help with local detail level adjustment -jm throw new UnsupportedOperationException("Not supported yet."); // NON-NLS //To change body of generated methods, choose Tools | Templates. } @Override - protected synchronized void dataItemRemoved(Data data, Series series) { + protected void dataItemRemoved(Data data, Series series) { EventCluster eventCluster = data.getYValue(); Platform.runLater(() -> { bundles.removeAll(eventCluster); @@ -373,7 +369,7 @@ public final class EventDetailsChart extends XYChart imp } @Override - protected synchronized void layoutPlotChildren() { + protected void layoutPlotChildren() { setCursor(Cursor.WAIT); maxY.set(0); if (bandByType.get()) { @@ -397,14 +393,14 @@ public final class EventDetailsChart extends XYChart imp } @Override - protected synchronized void seriesAdded(Series series, int i) { + protected void seriesAdded(Series series, int i) { for (int j = 0; j < series.getData().size(); j++) { dataItemAdded(series, j, series.getData().get(j)); } } @Override - protected synchronized void seriesRemoved(Series series) { + protected void seriesRemoved(Series series) { for (int j = 0; j < series.getData().size(); j++) { dataItemRemoved(series.getData().get(j), series); } @@ -529,22 +525,18 @@ public final class EventDetailsChart extends XYChart imp localMax = Math.max(yTop + h, localMax); if ((xLeft != bundleNode.getLayoutX()) || (yTop != bundleNode.getLayoutY())) { - - //animate node to new position - Timeline timeline = new Timeline(new KeyFrame(Duration.millis(100), - new KeyValue(bundleNode.layoutXProperty(), xLeft), - new KeyValue(bundleNode.layoutYProperty(), yTop)) - ); - timeline.setOnFinished((ActionEvent event) -> { - requestChartLayout(); - }); - timeline.play(); +// bundleNode.relocate(xLeft, yTop); +// requestChartLayout(); +// //animate node to new position + bundleNode.animateTo(xLeft, yTop); } } } return localMax; //return new max } + + private void bundleLayoutHelper(final EventBundleNodeBase bundleNode) { //make sure it is shown bundleNode.setVisible(true); @@ -713,5 +705,4 @@ public final class EventDetailsChart extends XYChart imp ); } } - } From b82d0a2e9cc3a4a43f74f4353793508dfaf90a04 Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 12 Nov 2015 15:37:39 -0500 Subject: [PATCH 5/8] return and cache event stripes at the DB layer --- .../timeline/datamodel/EventStripe.java | 17 ++++ .../datamodel/FilteredEventsModel.java | 4 +- .../autopsy/timeline/db/EventDB.java | 17 +++- .../autopsy/timeline/db/EventsRepository.java | 6 +- .../ui/AbstractVisualizationPane.java | 2 +- .../ui/detailview/DetailViewPane.java | 20 ++--- .../timeline/ui/detailview/EventAxis.java | 18 ++-- .../ui/detailview/EventClusterNode.java | 6 +- .../ui/detailview/EventDetailsChart.java | 83 +++++++++---------- 9 files changed, 98 insertions(+), 75 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java index cd457d336e..0b772cc6a2 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java @@ -83,6 +83,22 @@ public final class EventStripe implements EventBundle { */ private final Set hashHits = new HashSet<>(); + public EventStripe withParent(EventCluster parent) { + EventStripe eventStripe = new EventStripe(parent, this.type, this.description, this.lod); + eventStripe.clusters.addAll(clusters); + eventStripe.eventIDs.addAll(eventIDs); + eventStripe.tagged.addAll(tagged); + eventStripe.hashHits.addAll(hashHits); + return eventStripe; + } + + private EventStripe(EventCluster parent, EventType type, String description, DescriptionLoD lod) { + this.parent = parent; + this.type = type; + this.description = description; + this.lod = lod; + } + public EventStripe(EventCluster cluster, EventCluster parent) { clusters.add(cluster); @@ -158,4 +174,5 @@ public final class EventStripe implements EventBundle { public SortedSet< EventCluster> getClusters() { return Collections.unmodifiableSortedSet(clusters); } + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java index c11bb97012..b658e40c6a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java @@ -327,7 +327,7 @@ public final class FilteredEventsModel { * @return a list of event clusters at the requested zoom levels that are * within the requested time range and pass the requested filter */ - public List getEventClusters() { + public List getEventClusters() { final Interval range; final RootFilter filter; final EventTypeZoomLevel zoom; @@ -348,7 +348,7 @@ public final class FilteredEventsModel { * range and pass the requested filter, using the given aggregation * to control the grouping of events */ - public List getEventClusters(ZoomParams params) { + public List getEventClusters(ZoomParams params) { return repo.getEventClusters(params); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java index 1c1d3e3ab7..0c65ef7e98 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java @@ -48,6 +48,7 @@ import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; import org.joda.time.DateTimeZone; import org.joda.time.Interval; import org.joda.time.Period; @@ -55,6 +56,7 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.EventCluster; +import org.sleuthkit.autopsy.timeline.datamodel.EventStripe; import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.BaseTypes; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; @@ -1062,7 +1064,7 @@ public class EventDB { * the supplied filter, aggregated according to the given event type * and description zoom levels */ - List getClusteredEvents(ZoomParams params) { + List getClusteredEvents(ZoomParams params) { //unpack params Interval timeRange = params.getTimeRange(); RootFilter filter = params.getFilter(); @@ -1105,7 +1107,6 @@ public class EventDB { try (Statement createStatement = con.createStatement(); ResultSet rs = createStatement.executeQuery(query)) { while (rs.next()) { - events.add(eventClusterHelper(rs, useSubTypes, descriptionLOD, filter.getTagsFilter())); } } catch (SQLException ex) { @@ -1114,7 +1115,17 @@ public class EventDB { DBLock.unlock(); } - return mergeEventClusters(rangeInfo.getPeriodSize().getPeriod(), events); + List mergeEventClusters = mergeEventClusters(rangeInfo.getPeriodSize().getPeriod(), events); + + //merge clusters to stripes + Map, EventStripe> stripeDescMap = new HashMap<>(); + + for (EventCluster eventCluster : mergeEventClusters) { + stripeDescMap.merge(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription()), + new EventStripe(eventCluster, null), EventStripe::merge); + } + + return stripeDescMap.values().stream().sorted(Comparator.comparing(EventStripe::getStartMillis)).collect(Collectors.toList()); } /** diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java index 05d3551e05..ff20bd5df0 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java @@ -53,10 +53,8 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; -import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.timeline.CancellationProgressTask; import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.datamodel.EventCluster; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType; @@ -106,7 +104,7 @@ public class EventsRepository { private final LoadingCache minCache; private final LoadingCache idToEventCache; private final LoadingCache> eventCountsCache; - private final LoadingCache> eventClusterCache; + private final LoadingCache> eventClusterCache; private final ObservableMap datasourcesMap = FXCollections.observableHashMap(); private final ObservableMap hashSetMap = FXCollections.observableHashMap(); @@ -213,7 +211,7 @@ public class EventsRepository { } - synchronized public List getEventClusters(ZoomParams params) { + synchronized public List getEventClusters(ZoomParams params) { return eventClusterCache.getUnchecked(params); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java index e870b064f7..9d2f3bfb0f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java @@ -252,7 +252,7 @@ public abstract class AbstractVisualizationPane } }); - update(); +// update(); } @Subscribe diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java index 5517a0d415..ade7d4580e 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java @@ -66,7 +66,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.EventBundle; -import org.sleuthkit.autopsy.timeline.datamodel.EventCluster; +import org.sleuthkit.autopsy.timeline.datamodel.EventStripe; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; @@ -85,7 +85,7 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; * EventTypeMap, and dataSets is all linked directly to the ClusterChart which * must only be manipulated on the JavaFx thread. */ -public class DetailViewPane extends AbstractVisualizationPane, EventDetailsChart> { +public class DetailViewPane extends AbstractVisualizationPane, EventDetailsChart> { private final static Logger LOGGER = Logger.getLogger(DetailViewPane.class.getName()); @@ -93,7 +93,7 @@ public class DetailViewPane extends AbstractVisualizationPane verticalAxis = new EventAxis(); + private final Axis verticalAxis = new EventAxis(); private final ScrollBar vertScrollBar = new ScrollBar(); private final Region scrollBarSpacer = new Region(); @@ -101,7 +101,7 @@ public class DetailViewPane extends AbstractVisualizationPane> highlightedNodes = FXCollections.synchronizedObservableList(FXCollections.observableArrayList()); //private access to barchart data - private final Map> eventTypeToSeriesMap = new ConcurrentHashMap<>(); + private final Map> eventTypeToSeriesMap = new ConcurrentHashMap<>(); public ObservableList> getEventBundles() { return chart.getEventBundles(); @@ -224,7 +224,7 @@ public class DetailViewPane extends AbstractVisualizationPane getYAxis() { + protected Axis getYAxis() { return verticalAxis; } @@ -253,9 +253,9 @@ public class DetailViewPane extends AbstractVisualizationPane getSeries(final EventType et) { + private XYChart.Series getSeries(final EventType et) { return eventTypeToSeriesMap.computeIfAbsent(et, (EventType t) -> { - XYChart.Series series = new XYChart.Series<>(); + XYChart.Series series = new XYChart.Series<>(); series.setName(et.getDisplayName()); // Platform.runLater(() -> { dataSeries.add(series); @@ -310,7 +310,7 @@ public class DetailViewPane extends AbstractVisualizationPane eventClusters = filteredEvents.getEventClusters(); + List eventClusters = filteredEvents.getEventClusters(); final int size = eventClusters.size(); updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.updateUI")); @@ -318,9 +318,9 @@ public class DetailViewPane extends AbstractVisualizationPane xyData = new BarChart.Data<>(new DateTime(cluster.getStartMillis()), cluster); + final XYChart.Data xyData = new BarChart.Data<>(new DateTime(cluster.getStartMillis()), cluster); // Platform.runLater(() -> { getSeries(cluster.getEventType()).getData().add(xyData); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventAxis.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventAxis.java index 89b4de0ec7..544a5e9dd9 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventAxis.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventAxis.java @@ -22,21 +22,21 @@ import java.util.Collections; import java.util.List; import javafx.scene.chart.Axis; import javafx.scene.chart.XYChart; -import org.sleuthkit.autopsy.timeline.datamodel.EventCluster; +import org.sleuthkit.autopsy.timeline.datamodel.EventStripe; /** * No-Op axis that doesn't do anything usefull but is necessary to pass * AggregateEvent as the second member of {@link XYChart.Data} objects */ -class EventAxis extends Axis { +class EventAxis extends Axis { @Override - public double getDisplayPosition(EventCluster value) { + public double getDisplayPosition(EventStripe value) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override - public EventCluster getValueForDisplay(double displayPosition) { + public EventStripe getValueForDisplay(double displayPosition) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @@ -46,17 +46,17 @@ class EventAxis extends Axis { } @Override - public boolean isValueOnAxis(EventCluster value) { + public boolean isValueOnAxis(EventStripe value) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override - public double toNumericValue(EventCluster value) { + public double toNumericValue(EventStripe value) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override - public EventCluster toRealValue(double value) { + public EventStripe toRealValue(double value) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @@ -66,7 +66,7 @@ class EventAxis extends Axis { } @Override - protected List calculateTickValues(double length, Object range) { + protected List calculateTickValues(double length, Object range) { return Collections.emptyList(); } @@ -76,7 +76,7 @@ class EventAxis extends Axis { } @Override - protected String getTickMarkLabel(EventCluster value) { + protected String getTickMarkLabel(EventStripe value) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java index 02ff99d689..7f6ff32816 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java @@ -169,7 +169,7 @@ final public class EventClusterNode extends EventBundleNodeBase call() throws Exception { - Collection bundles; + Collection bundles = null; DescriptionLoD next = loadedDescriptionLoD; do { loadedDescriptionLoD = next; @@ -177,8 +177,8 @@ final public class EventClusterNode extends EventBundleNodeBase new EventStripe(eventCluster, getEventCluster()), //value + .collect(Collectors.toMap(EventStripe::getDescription, //key + eventStripe -> eventStripe.withParent(getEventCluster()), //value EventStripe::merge) //merge method ).values(); next = loadedDescriptionLoD.withRelativeDetail(relativeDetail); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java index 3ce076c544..e311e9a822 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java @@ -59,7 +59,6 @@ import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; import javafx.scene.shape.Line; import javafx.scene.shape.StrokeLineCap; -import org.apache.commons.lang3.tuple.ImmutablePair; import org.controlsfx.control.action.Action; import org.controlsfx.control.action.ActionUtils; import org.joda.time.DateTime; @@ -71,7 +70,6 @@ import org.sleuthkit.autopsy.timeline.datamodel.EventBundle; import org.sleuthkit.autopsy.timeline.datamodel.EventCluster; import org.sleuthkit.autopsy.timeline.datamodel.EventStripe; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; -import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.filters.AbstractFilter; import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter; import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; @@ -93,7 +91,7 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; * * //TODO: refactor the projected lines to a separate class. -jm */ -public final class EventDetailsChart extends XYChart implements TimeLineChart { +public final class EventDetailsChart extends XYChart implements TimeLineChart { private static final String styleSheet = GuideLine.class.getResource("EventsDetailsChart.css").toExternalForm(); private static final Image HIDE = new Image("/org/sleuthkit/autopsy/timeline/images/eye--minus.png"); // NON-NLS @@ -145,7 +143,7 @@ public final class EventDetailsChart extends XYChart imp */ private final Group nodeGroup = new Group(); private final ObservableList> bundles = FXCollections.observableArrayList(); - private final Map, EventStripe> stripeDescMap = new ConcurrentHashMap<>(); +// private final Map, EventStripe> stripeDescMap = new ConcurrentHashMap<>(); private final Map stripeNodeMap = new ConcurrentHashMap<>(); private final Map projectionMap = new ConcurrentHashMap<>(); @@ -181,7 +179,7 @@ public final class EventDetailsChart extends XYChart imp */ final SimpleDoubleProperty truncateWidth = new SimpleDoubleProperty(200.0); - EventDetailsChart(TimeLineController controller, DateAxis dateAxis, final Axis verticalAxis, ObservableList> selectedNodes) { + EventDetailsChart(TimeLineController controller, DateAxis dateAxis, final Axis verticalAxis, ObservableList> selectedNodes) { super(dateAxis, verticalAxis); this.controller = controller; @@ -321,55 +319,56 @@ public final class EventDetailsChart extends XYChart imp } @Override - protected synchronized void dataItemAdded(Series series, int i, Data data) { - final EventCluster eventCluster = data.getYValue(); - - EventStripe eventStripe = stripeDescMap.merge(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription()), - new EventStripe(eventCluster, null), - (EventStripe u, EventStripe v) -> { - EventStripeNode removeU = stripeNodeMap.remove(u); - EventStripeNode removeV = stripeNodeMap.remove(v); - Platform.runLater(() -> { - nodeGroup.getChildren().remove(removeU); - nodeGroup.getChildren().remove(removeV); - }); - return EventStripe.merge(u, v); - } - ); + protected synchronized void dataItemAdded(Series series, int i, Data data) { + final EventStripe eventStripe = data.getYValue(); +// +// EventStripe eventStripe = stripeDescMap.put(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription()), eventCluster); +//// new EventStripe(eventCluster, null), +//// (EventStripe u, EventStripe v) -> { +//// EventStripeNode removeU = stripeNodeMap.remove(u); +//// EventStripeNode removeV = stripeNodeMap.remove(v); +//// Platform.runLater(() -> { +//// nodeGroup.getChildren().remove(removeU); +//// nodeGroup.getChildren().remove(removeV); +//// }); +//// return EventStripe.merge(u, v); +//// } +//// ); EventStripeNode stripeNode = new EventStripeNode(EventDetailsChart.this, eventStripe, null); stripeNodeMap.put(eventStripe, stripeNode); Platform.runLater(() -> { - bundles.add(eventCluster); + bundles.add(eventStripe); nodeGroup.getChildren().add(stripeNode); data.setNode(stripeNode); }); } @Override - protected void dataItemChanged(Data data) { + protected void dataItemChanged(Data data) { //TODO: can we use this to help with local detail level adjustment -jm throw new UnsupportedOperationException("Not supported yet."); // NON-NLS //To change body of generated methods, choose Tools | Templates. } @Override - protected void dataItemRemoved(Data data, Series series) { - EventCluster eventCluster = data.getYValue(); - Platform.runLater(() -> { - bundles.removeAll(eventCluster); - }); + protected synchronized void dataItemRemoved(Data data, Series series) { + EventStripe removedStripe = data.getYValue(); +// Platform.runLater(() -> { +// bundles.removeAll(removedStripe); +// }); - EventStripe removedStripe = stripeDescMap.remove(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription())); - if (removedStripe != null) { - EventStripeNode removedNode = stripeNodeMap.remove(removedStripe); - Platform.runLater(() -> { - nodeGroup.getChildren().remove(removedNode); - data.setNode(null); - }); - } +// EventStripe removedStripe = stripeDescMap.remove(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription())); +// if (removedStripe != null) { + EventStripeNode removedNode = stripeNodeMap.remove(removedStripe); + Platform.runLater(() -> { + bundles.removeAll(removedStripe); + nodeGroup.getChildren().remove(removedNode); + data.setNode(null); + }); +// } } @Override - protected void layoutPlotChildren() { + protected synchronized void layoutPlotChildren() { setCursor(Cursor.WAIT); maxY.set(0); if (bandByType.get()) { @@ -393,16 +392,16 @@ public final class EventDetailsChart extends XYChart imp } @Override - protected void seriesAdded(Series series, int i) { + protected void seriesAdded(Series series, int i) { for (int j = 0; j < series.getData().size(); j++) { dataItemAdded(series, j, series.getData().get(j)); } } @Override - protected void seriesRemoved(Series series) { - for (int j = 0; j < series.getData().size(); j++) { - dataItemRemoved(series.getData().get(j), series); + protected void seriesRemoved(Series series) { + for (Data data : series.getData()) { + dataItemRemoved(data, series); } } @@ -413,7 +412,7 @@ public final class EventDetailsChart extends XYChart imp /** * @return all the nodes that pass the given predicate */ - Iterable> getNodes(Predicate> p) { + synchronized Iterable> getNodes(Predicate> p) { //use this recursive function to flatten the tree of nodes into an iterable. Function, Stream>> stripeFlattener = new Function, Stream>>() { @@ -535,8 +534,6 @@ public final class EventDetailsChart extends XYChart imp return localMax; //return new max } - - private void bundleLayoutHelper(final EventBundleNodeBase bundleNode) { //make sure it is shown bundleNode.setVisible(true); From 1873bef59d4fee9da5f2446be8bac3dfe1d02266 Mon Sep 17 00:00:00 2001 From: jmillman Date: Wed, 18 Nov 2015 16:56:34 -0500 Subject: [PATCH 6/8] concurrentskiplistset and hashCode + equals. WIP --- .../timeline/datamodel/EventCluster.java | 43 +++++++++++ .../timeline/datamodel/EventStripe.java | 39 ++++++++++ .../ui/detailview/DetailViewPane.java | 4 +- .../ui/detailview/EventBundleNodeBase.java | 6 +- .../ui/detailview/EventDetailsChart.java | 77 ++++++++----------- .../ui/detailview/EventStripeNode.java | 19 ++++- 6 files changed, 132 insertions(+), 56 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventCluster.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventCluster.java index 592c4674f6..6df77bd402 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventCluster.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventCluster.java @@ -188,4 +188,47 @@ public class EventCluster implements EventBundle { public SortedSet< EventCluster> getClusters() { return ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis)).add(this).build(); } + + @Override + public int hashCode() { + int hash = 7; + hash = 89 * hash + Objects.hashCode(this.parent); + hash = 89 * hash + Objects.hashCode(this.span); + hash = 89 * hash + Objects.hashCode(this.type); + hash = 89 * hash + Objects.hashCode(this.description); + hash = 89 * hash + Objects.hashCode(this.lod); + hash = 89 * hash + Objects.hashCode(this.eventIDs); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final EventCluster other = (EventCluster) obj; + if (!Objects.equals(this.parent, other.parent)) { + return false; + } + if (!Objects.equals(this.span, other.span)) { + return false; + } + if (!Objects.equals(this.type, other.type)) { + return false; + } + if (!Objects.equals(this.description, other.description)) { + return false; + } + if (this.lod != other.lod) { + return false; + } + if (!Objects.equals(this.eventIDs, other.eventIDs)) { + return false; + } + return true; + } + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java index 0b772cc6a2..ed4ee4886b 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java @@ -171,8 +171,47 @@ public final class EventStripe implements EventBundle { return clusters.last().getEndMillis(); } + @Override public SortedSet< EventCluster> getClusters() { return Collections.unmodifiableSortedSet(clusters); } + @Override + public int hashCode() { + int hash = 7; + hash = 37 * hash + java.util.Objects.hashCode(this.parent); + hash = 37 * hash + java.util.Objects.hashCode(this.type); + hash = 37 * hash + java.util.Objects.hashCode(this.description); + hash = 37 * hash + java.util.Objects.hashCode(this.lod); + hash = 37 * hash + java.util.Objects.hashCode(this.eventIDs); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final EventStripe other = (EventStripe) obj; + if (!java.util.Objects.equals(this.parent, other.parent)) { + return false; + } + if (!java.util.Objects.equals(this.type, other.type)) { + return false; + } + if (!java.util.Objects.equals(this.description, other.description)) { + return false; + } + if (this.lod != other.lod) { + return false; + } + if (!java.util.Objects.equals(this.eventIDs, other.eventIDs)) { + return false; + } + return true; + } + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java index ade7d4580e..86b0c7d8cf 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java @@ -257,10 +257,7 @@ public class DetailViewPane extends AbstractVisualizationPane { XYChart.Series series = new XYChart.Series<>(); series.setName(et.getDisplayName()); -// Platform.runLater(() -> { dataSeries.add(series); -// }); - return series; }); } @@ -300,6 +297,7 @@ public class DetailViewPane extends AbstractVisualizationPane { - chart.requestChartLayout(); - }); + timeline.setOnFinished(finished -> Platform.runLater(chart::requestChartLayout)); timeline.play(); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java index e311e9a822..33e45c047e 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailsChart.java @@ -24,11 +24,12 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.List; import java.util.Map; import java.util.MissingResourceException; +import java.util.NavigableSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListSet; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -105,6 +106,7 @@ public final class EventDetailsChart extends XYChart impl private final FilteredEventsModel filteredEvents; private ContextMenu chartContextMenu; + private Set activeQuickHidefilters; @Override public ContextMenu getChartContextMenu() { @@ -143,8 +145,7 @@ public final class EventDetailsChart extends XYChart impl */ private final Group nodeGroup = new Group(); private final ObservableList> bundles = FXCollections.observableArrayList(); -// private final Map, EventStripe> stripeDescMap = new ConcurrentHashMap<>(); - private final Map stripeNodeMap = new ConcurrentHashMap<>(); + private final NavigableSet< EventStripeNode> stripeNodes = new ConcurrentSkipListSet<>(Comparator.comparing(EventStripeNode::getStartMillis)); private final Map projectionMap = new ConcurrentHashMap<>(); /** @@ -319,23 +320,11 @@ public final class EventDetailsChart extends XYChart impl } @Override - protected synchronized void dataItemAdded(Series series, int i, Data data) { + protected void dataItemAdded(Series series, int i, Data data) { final EventStripe eventStripe = data.getYValue(); -// -// EventStripe eventStripe = stripeDescMap.put(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription()), eventCluster); -//// new EventStripe(eventCluster, null), -//// (EventStripe u, EventStripe v) -> { -//// EventStripeNode removeU = stripeNodeMap.remove(u); -//// EventStripeNode removeV = stripeNodeMap.remove(v); -//// Platform.runLater(() -> { -//// nodeGroup.getChildren().remove(removeU); -//// nodeGroup.getChildren().remove(removeV); -//// }); -//// return EventStripe.merge(u, v); -//// } -//// ); + EventStripeNode stripeNode = new EventStripeNode(EventDetailsChart.this, eventStripe, null); - stripeNodeMap.put(eventStripe, stripeNode); + stripeNodes.add(stripeNode); Platform.runLater(() -> { bundles.add(eventStripe); nodeGroup.getChildren().add(stripeNode); @@ -350,41 +339,39 @@ public final class EventDetailsChart extends XYChart impl } @Override - protected synchronized void dataItemRemoved(Data data, Series series) { + protected void dataItemRemoved(Data data, Series series) { EventStripe removedStripe = data.getYValue(); -// Platform.runLater(() -> { -// bundles.removeAll(removedStripe); -// }); -// EventStripe removedStripe = stripeDescMap.remove(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription())); -// if (removedStripe != null) { - EventStripeNode removedNode = stripeNodeMap.remove(removedStripe); + EventStripeNode removedNode = (EventStripeNode) data.getNode(); + boolean remove = stripeNodes.remove(removedNode); Platform.runLater(() -> { bundles.removeAll(removedStripe); - nodeGroup.getChildren().remove(removedNode); + nodeGroup.getChildren().removeAll(removedNode); data.setNode(null); }); // } } @Override - protected synchronized void layoutPlotChildren() { + protected synchronized void layoutPlotChildren() { setCursor(Cursor.WAIT); maxY.set(0); + + activeQuickHidefilters = getController().getQuickHideFilters().stream() + .filter(AbstractFilter::isActive) + .map(DescriptionFilter::getDescription) + .collect(Collectors.toSet()); if (bandByType.get()) { - stripeNodeMap.values().stream() + stripeNodes.stream() .collect(Collectors.groupingBy(EventStripeNode::getEventType)).values() .forEach(inputNodes -> { - List stripeNodes = inputNodes.stream() - .sorted(Comparator.comparing(EventStripeNode::getStartMillis)) - .collect(Collectors.toList()); +// List sortedStripNodesForType = inputNodes.stream() +// .sorted(Comparator.comparing(EventStripeNode::getStartMillis)) +// .collect(Collectors.toList()); - maxY.set(layoutEventBundleNodes(stripeNodes, maxY.get())); + maxY.set(layoutEventBundleNodes(inputNodes, maxY.get())); }); } else { - List stripeNodes = stripeNodeMap.values().stream() - .sorted(Comparator.comparing(EventStripeNode::getStartMillis)) - .collect(Collectors.toList()); maxY.set(layoutEventBundleNodes(stripeNodes, 0)); } layoutProjectionMap(); @@ -393,16 +380,16 @@ public final class EventDetailsChart extends XYChart impl @Override protected void seriesAdded(Series series, int i) { - for (int j = 0; j < series.getData().size(); j++) { - dataItemAdded(series, j, series.getData().get(j)); - } +// for (int j = 0; j < series.getData().size(); j++) { +// dataItemAdded(series, j, series.getData().get(j)); +// } } @Override protected void seriesRemoved(Series series) { - for (Data data : series.getData()) { - dataItemRemoved(data, series); - } +// for (Data data : series.getData()) { +// dataItemRemoved(data, series); +// } } ReadOnlyDoubleProperty maxVScrollProperty() { @@ -412,7 +399,7 @@ public final class EventDetailsChart extends XYChart impl /** * @return all the nodes that pass the given predicate */ - synchronized Iterable> getNodes(Predicate> p) { + synchronized Iterable> getNodes(Predicate> p) { //use this recursive function to flatten the tree of nodes into an iterable. Function, Stream>> stripeFlattener = new Function, Stream>>() { @@ -424,7 +411,7 @@ public final class EventDetailsChart extends XYChart impl } }; - return stripeNodeMap.values().stream() + return stripeNodes.stream() .flatMap(stripeFlattener) .filter(p).collect(Collectors.toList()); } @@ -471,10 +458,6 @@ public final class EventDetailsChart extends XYChart impl // maximum y values occupied by any of the given nodes, updated as nodes are layed out. double localMax = minY; - Set activeQuickHidefilters = getController().getQuickHideFilters().stream() - .filter(AbstractFilter::isActive) - .map(DescriptionFilter::getDescription) - .collect(Collectors.toSet()); //for each node do a recursive layout to size it and then position it in first available slot for (EventBundleNodeBase bundleNode : nodes) { //is the node hiden by a quick hide filter? diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventStripeNode.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventStripeNode.java index 2a785bdca0..fdc77c9fbc 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventStripeNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventStripeNode.java @@ -98,8 +98,6 @@ final public class EventStripeNode extends EventBundleNodeBase Date: Thu, 19 Nov 2015 12:03:58 -0500 Subject: [PATCH 7/8] concurrentskiplistset and hashCode + equals. WIP --- .../src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java index ff20bd5df0..4ac1e73135 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java @@ -54,7 +54,9 @@ import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.CancellationProgressTask; +import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.timeline.TimeLineController; +import org.sleuthkit.autopsy.timeline.datamodel.EventStripe; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType; From 92d3a1271bc3322034478ffd132dd489af294a51 Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 19 Nov 2015 15:32:03 -0500 Subject: [PATCH 8/8] revert some of the threading changes, remove unused hashcode and equals; cleanup --- .../timeline/datamodel/EventCluster.java | 42 ------------ .../timeline/datamodel/EventStripe.java | 36 +--------- .../datamodel/FilteredEventsModel.java | 6 +- .../autopsy/timeline/db/EventDB.java | 6 +- .../autopsy/timeline/db/EventsRepository.java | 23 ++++--- .../timeline/ui/detailview/Bundle.properties | 4 +- .../ui/detailview/DetailViewPane.java | 67 +++++++----------- .../ui/detailview/EventClusterNode.java | 4 +- .../ui/detailview/EventDetailsChart.java | 68 ++++++++----------- .../ui/detailview/EventStripeNode.java | 16 ----- .../ui/detailview/tree/EventsTree.java | 4 +- 11 files changed, 81 insertions(+), 195 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventCluster.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventCluster.java index 6df77bd402..975b0cc40f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventCluster.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventCluster.java @@ -189,46 +189,4 @@ public class EventCluster implements EventBundle { return ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis)).add(this).build(); } - @Override - public int hashCode() { - int hash = 7; - hash = 89 * hash + Objects.hashCode(this.parent); - hash = 89 * hash + Objects.hashCode(this.span); - hash = 89 * hash + Objects.hashCode(this.type); - hash = 89 * hash + Objects.hashCode(this.description); - hash = 89 * hash + Objects.hashCode(this.lod); - hash = 89 * hash + Objects.hashCode(this.eventIDs); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final EventCluster other = (EventCluster) obj; - if (!Objects.equals(this.parent, other.parent)) { - return false; - } - if (!Objects.equals(this.span, other.span)) { - return false; - } - if (!Objects.equals(this.type, other.type)) { - return false; - } - if (!Objects.equals(this.description, other.description)) { - return false; - } - if (this.lod != other.lod) { - return false; - } - if (!Objects.equals(this.eventIDs, other.eventIDs)) { - return false; - } - return true; - } - } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java index ed4ee4886b..f46cbd82da 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/EventStripe.java @@ -177,41 +177,9 @@ public final class EventStripe implements EventBundle { } @Override - public int hashCode() { - int hash = 7; - hash = 37 * hash + java.util.Objects.hashCode(this.parent); - hash = 37 * hash + java.util.Objects.hashCode(this.type); - hash = 37 * hash + java.util.Objects.hashCode(this.description); - hash = 37 * hash + java.util.Objects.hashCode(this.lod); - hash = 37 * hash + java.util.Objects.hashCode(this.eventIDs); - return hash; + public String toString() { + return "EventStripe{" + "description=" + description + ", eventIDs=" + eventIDs.size() + '}'; } - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final EventStripe other = (EventStripe) obj; - if (!java.util.Objects.equals(this.parent, other.parent)) { - return false; - } - if (!java.util.Objects.equals(this.type, other.type)) { - return false; - } - if (!java.util.Objects.equals(this.description, other.description)) { - return false; - } - if (this.lod != other.lod) { - return false; - } - if (!java.util.Objects.equals(this.eventIDs, other.eventIDs)) { - return false; - } - return true; - } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java index b658e40c6a..f3764b622a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java @@ -327,7 +327,7 @@ public final class FilteredEventsModel { * @return a list of event clusters at the requested zoom levels that are * within the requested time range and pass the requested filter */ - public List getEventClusters() { + public List getEventStripes() { final Interval range; final RootFilter filter; final EventTypeZoomLevel zoom; @@ -338,7 +338,7 @@ public final class FilteredEventsModel { zoom = requestedTypeZoom.get(); lod = requestedLOD.get(); } - return repo.getEventClusters(new ZoomParams(range, zoom, filter, lod)); + return repo.getEventStripes(new ZoomParams(range, zoom, filter, lod)); } /** @@ -349,7 +349,7 @@ public final class FilteredEventsModel { * to control the grouping of events */ public List getEventClusters(ZoomParams params) { - return repo.getEventClusters(params); + return repo.getEventStripes(params); } synchronized public boolean handleContentTagAdded(ContentTagAddedEvent evt) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java index 0c65ef7e98..dff5e7aee7 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java @@ -1054,8 +1054,7 @@ public class EventDB { } /** - * get a list of {@link EventCluster}s, clustered according to the given - * zoom paramaters. + * get a list of {@link EventStripe}s, clustered according to the given * zoom paramaters. * * @param params the zoom params that determine the zooming, filtering and * clustering. @@ -1064,7 +1063,7 @@ public class EventDB { * the supplied filter, aggregated according to the given event type * and description zoom levels */ - List getClusteredEvents(ZoomParams params) { + List getEventStripes(ZoomParams params) { //unpack params Interval timeRange = params.getTimeRange(); RootFilter filter = params.getFilter(); @@ -1103,7 +1102,6 @@ public class EventDB { List events = new ArrayList<>(); DBLock.lock(); - try (Statement createStatement = con.createStatement(); ResultSet rs = createStatement.executeQuery(query)) { while (rs.next()) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java index 4ac1e73135..4aa7dfacf2 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java @@ -31,6 +31,7 @@ import java.util.Map; import static java.util.Objects.isNull; import java.util.Set; import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -48,13 +49,14 @@ import org.apache.commons.lang3.StringUtils; import org.joda.time.Interval; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; -import org.sleuthkit.autopsy.timeline.CancellationProgressTask; import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.timeline.CancellationProgressTask; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.EventStripe; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; @@ -106,7 +108,7 @@ public class EventsRepository { private final LoadingCache minCache; private final LoadingCache idToEventCache; private final LoadingCache> eventCountsCache; - private final LoadingCache> eventClusterCache; + private final LoadingCache> eventStripeCache; private final ObservableMap datasourcesMap = FXCollections.observableHashMap(); private final ObservableMap hashSetMap = FXCollections.observableHashMap(); @@ -153,10 +155,10 @@ public class EventsRepository { .maximumSize(1000L) .expireAfterAccess(10, TimeUnit.MINUTES) .build(CacheLoader.from(eventDB::countEventsByType)); - eventClusterCache = CacheBuilder.newBuilder() + eventStripeCache = CacheBuilder.newBuilder() .maximumSize(1000L) .expireAfterAccess(10, TimeUnit.MINUTES - ).build(CacheLoader.from(eventDB::getClusteredEvents)); + ).build(CacheLoader.from(eventDB::getEventStripes)); maxCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMaxTime)); minCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMinTime)); this.modelInstance = new FilteredEventsModel(this, currentStateProperty); @@ -213,8 +215,13 @@ public class EventsRepository { } - synchronized public List getEventClusters(ZoomParams params) { - return eventClusterCache.getUnchecked(params); + synchronized public List getEventStripes(ZoomParams params) { + try { + return eventStripeCache.get(params); + } catch (ExecutionException ex) { + Exceptions.printStackTrace(ex); + return Collections.emptyList(); + } } synchronized public Map countEvents(ZoomParams params) { @@ -225,7 +232,7 @@ public class EventsRepository { minCache.invalidateAll(); maxCache.invalidateAll(); eventCountsCache.invalidateAll(); - eventClusterCache.invalidateAll(); + eventStripeCache.invalidateAll(); idToEventCache.invalidateAll(); } @@ -299,7 +306,7 @@ public class EventsRepository { synchronized private void invalidateCaches(Set updatedEventIDs) { eventCountsCache.invalidateAll(); - eventClusterCache.invalidateAll(); + eventStripeCache.invalidateAll(); idToEventCache.invalidateAll(updatedEventIDs); try { tagNames.setAll(autoCase.getSleuthkitCase().getTagNamesInUse()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/Bundle.properties b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/Bundle.properties index 77bc1dd893..f7e034339e 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/Bundle.properties @@ -6,8 +6,8 @@ DetailViewPane.truncateSliderLabel.text=max description width (px)\: DetailViewPane.advancedLayoutOptionsButtonLabel.text=Advanced Layout Options DetailViewPane.bandByTypeBox.text=Band by Type DetailViewPane.bandByTypeBoxMenuItem.text=Band by Type -DetailViewPane.oneEventPerRowBox.text=One Event Per Row -DetailViewPane.oneEventPerRowBoxMenuItem.text=One Event Per Row +DetailViewPane.oneEventPerRowBox.text=One Per Row +DetailViewPane.oneEventPerRowBoxMenuItem.text=One Per Row DetailViewPan.truncateAllBox.text=Truncate Descriptions DetailViewPan.truncateAllBoxMenuItem.text=Truncate Descriptions DetailViewPane.truncateSlideLabelMenuItem.text=max description width (px) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java index 86b0c7d8cf..ab2ed5d2d3 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java @@ -19,9 +19,9 @@ package org.sleuthkit.autopsy.timeline.ui.detailview; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.Observable; @@ -31,8 +31,8 @@ import javafx.collections.ObservableList; import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.geometry.Orientation; +import javafx.scene.Cursor; import javafx.scene.chart.Axis; -import javafx.scene.chart.BarChart; import javafx.scene.chart.XYChart; import javafx.scene.control.CheckBox; import javafx.scene.control.CustomMenuItem; @@ -63,6 +63,7 @@ import org.joda.time.DateTime; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.LoggedTask; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.EventBundle; @@ -101,10 +102,10 @@ public class DetailViewPane extends AbstractVisualizationPane> highlightedNodes = FXCollections.synchronizedObservableList(FXCollections.observableArrayList()); //private access to barchart data - private final Map> eventTypeToSeriesMap = new ConcurrentHashMap<>(); + private final Map> eventTypeToSeriesMap = new HashMap<>(); - public ObservableList> getEventBundles() { - return chart.getEventBundles(); + public ObservableList getEventStripes() { + return chart.getEventStripes(); } public DetailViewPane(TimeLineController controller, Pane partPane, Pane contextPane, Region bottomLeftSpacer) { @@ -252,11 +253,11 @@ public class DetailViewPane extends AbstractVisualizationPane getSeries(final EventType et) { - return eventTypeToSeriesMap.computeIfAbsent(et, (EventType t) -> { + return eventTypeToSeriesMap.computeIfAbsent(et, eventType -> { XYChart.Series series = new XYChart.Series<>(); - series.setName(et.getDisplayName()); + series.setName(eventType.getDisplayName()); dataSeries.add(series); return series; }); @@ -272,18 +273,6 @@ public class DetailViewPane extends AbstractVisualizationPane { + dataSeries.clear(); + dateAxis.setLowerBound(new DateTime(lowerBound, TimeLineController.getJodaTimeZone())); + dateAxis.setUpperBound(new DateTime(upperBound, TimeLineController.getJodaTimeZone())); + vertScrollBar.setValue(0); + eventTypeToSeriesMap.clear(); }); - List eventClusters = filteredEvents.getEventClusters(); + List eventStripes = filteredEvents.getEventStripes(); - final int size = eventClusters.size(); + final int size = eventStripes.size(); updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.updateUI")); for (int i = 0; i < size; i++) { if (isCancelled()) { break; } - final EventStripe cluster = eventClusters.get(i); updateProgress(i, size); - final XYChart.Data xyData = new BarChart.Data<>(new DateTime(cluster.getStartMillis()), cluster); - -// Platform.runLater(() -> { - getSeries(cluster.getEventType()).getData().add(xyData); -// }); + final EventStripe cluster = eventStripes.get(i); + final XYChart.Data xyData = new XYChart.Data<>(new DateTime(cluster.getStartMillis()), cluster); + Platform.runLater(() -> { + getSeries(cluster.getEventType()).getData().add(xyData); + }); } + updateProgress(1, 1); - return eventClusters.isEmpty() == false; + + return eventStripes.isEmpty() == false; } @Override protected void succeeded() { super.succeeded(); layoutDateLabels(); + setCursor(Cursor.DEFAULT); } }; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java index 7f6ff32816..371f70e8b9 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java @@ -141,7 +141,7 @@ final public class EventClusterNode extends EventBundleNodeBase + chart.getEventStripes().removeIf(bundle -> subNodes.stream().anyMatch(subNode -> bundle.equals(subNode.getEventStripe())) ); @@ -200,7 +200,7 @@ final public class EventClusterNode extends EventBundleNodeBase impl * by allowing a single translation of this group. */ private final Group nodeGroup = new Group(); - private final ObservableList> bundles = FXCollections.observableArrayList(); - private final NavigableSet< EventStripeNode> stripeNodes = new ConcurrentSkipListSet<>(Comparator.comparing(EventStripeNode::getStartMillis)); + private final ObservableList bundles = FXCollections.observableArrayList(); + private final ObservableList< EventStripeNode> stripeNodes = FXCollections.observableArrayList(); + private final ObservableList< EventStripeNode> sortedStripeNodes = stripeNodes.sorted(Comparator.comparing(EventStripeNode::getStartMillis)); private final Map projectionMap = new ConcurrentHashMap<>(); /** @@ -239,7 +236,7 @@ public final class EventDetailsChart extends XYChart impl this.selectedNodes.addListener(new SelectionChangeHandler()); } - ObservableList> getEventBundles() { + ObservableList getEventStripes() { return bundles; } @@ -319,17 +316,17 @@ public final class EventDetailsChart extends XYChart impl return descrVisibility; } + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) @Override - protected void dataItemAdded(Series series, int i, Data data) { + protected void dataItemAdded(Series series, int i, Data data) { final EventStripe eventStripe = data.getYValue(); + bundles.add(eventStripe); EventStripeNode stripeNode = new EventStripeNode(EventDetailsChart.this, eventStripe, null); stripeNodes.add(stripeNode); - Platform.runLater(() -> { - bundles.add(eventStripe); - nodeGroup.getChildren().add(stripeNode); - data.setNode(stripeNode); - }); + nodeGroup.getChildren().add(stripeNode); + data.setNode(stripeNode); + } @Override @@ -338,41 +335,32 @@ public final class EventDetailsChart extends XYChart impl throw new UnsupportedOperationException("Not supported yet."); // NON-NLS //To change body of generated methods, choose Tools | Templates. } + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) @Override - protected void dataItemRemoved(Data data, Series series) { + protected void dataItemRemoved(Data data, Series series) { EventStripe removedStripe = data.getYValue(); - + bundles.removeAll(removedStripe); EventStripeNode removedNode = (EventStripeNode) data.getNode(); - boolean remove = stripeNodes.remove(removedNode); - Platform.runLater(() -> { - bundles.removeAll(removedStripe); - nodeGroup.getChildren().removeAll(removedNode); - data.setNode(null); - }); -// } + stripeNodes.removeAll(removedNode); + nodeGroup.getChildren().removeAll(removedNode); + data.setNode(null); } @Override - protected synchronized void layoutPlotChildren() { + protected void layoutPlotChildren() { setCursor(Cursor.WAIT); maxY.set(0); - activeQuickHidefilters = getController().getQuickHideFilters().stream() .filter(AbstractFilter::isActive) .map(DescriptionFilter::getDescription) .collect(Collectors.toSet()); - if (bandByType.get()) { - stripeNodes.stream() - .collect(Collectors.groupingBy(EventStripeNode::getEventType)).values() - .forEach(inputNodes -> { -// List sortedStripNodesForType = inputNodes.stream() -// .sorted(Comparator.comparing(EventStripeNode::getStartMillis)) -// .collect(Collectors.toList()); - maxY.set(layoutEventBundleNodes(inputNodes, maxY.get())); - }); + if (bandByType.get()) { + sortedStripeNodes.stream() + .collect(Collectors.groupingBy(EventStripeNode::getEventType)).values() + .forEach(inputNodes -> maxY.set(layoutEventBundleNodes(inputNodes, maxY.get()))); } else { - maxY.set(layoutEventBundleNodes(stripeNodes, 0)); + maxY.set(layoutEventBundleNodes(sortedStripeNodes.sorted(Comparator.comparing(EventStripeNode::getStartMillis)), 0)); } layoutProjectionMap(); setCursor(null); @@ -380,16 +368,14 @@ public final class EventDetailsChart extends XYChart impl @Override protected void seriesAdded(Series series, int i) { -// for (int j = 0; j < series.getData().size(); j++) { -// dataItemAdded(series, j, series.getData().get(j)); -// } + } @Override protected void seriesRemoved(Series series) { -// for (Data data : series.getData()) { -// dataItemRemoved(data, series); -// } + for (Data data : series.getData()) { + dataItemRemoved(data, series); + } } ReadOnlyDoubleProperty maxVScrollProperty() { @@ -411,7 +397,7 @@ public final class EventDetailsChart extends XYChart impl } }; - return stripeNodes.stream() + return sortedStripeNodes.stream() .flatMap(stripeFlattener) .filter(p).collect(Collectors.toList()); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventStripeNode.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventStripeNode.java index fdc77c9fbc..9620fcabdd 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventStripeNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventStripeNode.java @@ -178,21 +178,5 @@ final public class EventStripeNode extends EventBundleNodeBase> c) -> { + detailViewPane.getEventStripes().addListener((ListChangeListener.Change> c) -> { while (c.next()) { for (EventBundle bundle : c.getAddedSubList()) { getRoot().insert(bundle); @@ -114,7 +114,7 @@ final public class EventsTree extends BorderPane { @ThreadConfined(type = ThreadConfined.ThreadType.JFX) private void setRoot() { RootItem root = new RootItem(); - for (EventBundle bundle : detailViewPane.getEventBundles()) { + for (EventBundle bundle : detailViewPane.getEventStripes()) { root.insert(bundle); } root.resort(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()));