cleanup, comments

This commit is contained in:
jmillman 2015-12-01 16:07:58 -05:00
parent 67dbaa4cdc
commit 9cd5800ce7
5 changed files with 159 additions and 104 deletions

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Copyright 2014-15 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -33,6 +33,7 @@ import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
@ -75,7 +76,7 @@ import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent;
* @param <Y> the type of data plotted along the y axis
* @param <NodeType> the type of nodes used to represent data items
* @param <ChartType> the type of the {@link XYChart<X,Y>} this class uses to
* plot the * data.
* plot the data.
*
* TODO: this is becoming (too?) closely tied to the notion that their is a
* {@link XYChart} doing the rendering. Is this a good idea? -jm TODO: pull up
@ -97,16 +98,13 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
* access to chart data via series
*/
protected final ObservableList<XYChart.Series<X, Y>> dataSeries = FXCollections.<XYChart.Series<X, Y>>observableArrayList();
abstract protected void resetData();
protected final Map<EventType, XYChart.Series<X, Y>> eventTypeToSeriesMap = new HashMap<>();
protected ChartType chart;
//// replacement axis label componenets
private final Pane leafPane; // container for the leaf lables in the declutterd axis
private final Pane branchPane;// container for the branch lables in the declutterd axis
protected final Region spacer;
/**
@ -148,7 +146,7 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
* @return true if the tick label for the given value should be bold ( has
* relevant data), false* otherwise
*/
protected abstract Boolean isTickBold(X value);
abstract protected Boolean isTickBold(X value);
/**
* apply this visualization's 'selection effect' to the given node
@ -157,27 +155,27 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
* @param applied true if the effect should be applied, false if the effect
* should
*/
protected abstract void applySelectionEffect(NodeType node, Boolean applied);
abstract protected void applySelectionEffect(NodeType node, Boolean applied);
/**
* @return a task to execute on a background thread to reload this
* visualization with different data.
*/
protected abstract Task<Boolean> getUpdateTask();
abstract protected Task<Boolean> getUpdateTask();
/**
* @return return the {@link Effect} applied to 'selected nodes' in this
* visualization, or null if selection is visualized via another
* mechanism
*/
protected abstract Effect getSelectionEffect();
abstract protected Effect getSelectionEffect();
/**
* @param tickValue
*
* @return a String to use for a tick mark label given a tick value
*/
protected abstract String getTickMarkLabel(X tickValue);
abstract protected String getTickMarkLabel(X tickValue);
/**
* the spacing (in pixels) between tick marks of the horizontal axis. This
@ -185,22 +183,27 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
*
* @return the spacing in pixels between tick marks of the horizontal axis
*/
protected abstract double getTickSpacing();
abstract protected double getTickSpacing();
/**
* @return the horizontal axis used by this Visualization's chart
*/
protected abstract Axis<X> getXAxis();
abstract protected Axis<X> getXAxis();
/**
* @return the vertical axis used by this Visualization's chart
*/
protected abstract Axis<Y> getYAxis();
abstract protected Axis<Y> getYAxis();
abstract protected void resetData();
/**
* update this visualization based on current state of zoom / filters.
* Primarily this invokes the background {@link Task} returned by
* {@link #getUpdateTask()} which derived classes must implement.
* Primarily this invokes the background {@link VisualizationUpdateTask}
* returned by {@link #getUpdateTask()}, which derived classes must
* implement.
*
* TODO: replace this logic with a {@link Service} ? -jm
*/
final synchronized public void update() {
if (updateTask != null) {
@ -236,8 +239,10 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
invalidationListener = null;
}
/**
* make a series for each event type in a consistent order
*/
protected final void createSeries() {
//make all series to ensure they get created in consistent order
for (EventType eventType : EventType.allTypes) {
XYChart.Series<X, Y> series = new XYChart.Series<>();
series.setName(eventType.getDisplayName());
@ -246,9 +251,19 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
}
}
/**
*
* @param et the EventType to get the series for
*
* @return a Series object to contain all the events with the given
* EventType
*/
protected final XYChart.Series<X, Y> getSeries(final EventType et) {
return eventTypeToSeriesMap.get(et);
}
protected AbstractVisualizationPane(TimeLineController controller, Pane partPane, Pane contextPane, Region spacer) {
this.controller = controller;
this.filteredEvents = controller.getEventsModel();
this.filteredEvents.registerForEvents(this);
this.filteredEvents.zoomParametersProperty().addListener(invalidationListener);
@ -268,18 +283,10 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
TimeLineController.getTimeZone().addListener(invalidationListener);
//show tooltip text in status bar
hoverProperty().addListener((observable, oldActivated, newActivated) -> {
if (newActivated) {
controller.setStatus(DEFAULT_TOOLTIP.getText());
} else {
controller.setStatus("");
}
});
hoverProperty().addListener(observable -> controller.setStatus(isHover() ? DEFAULT_TOOLTIP.getText() : ""));
}
protected final Map<EventType, XYChart.Series<X, Y>> eventTypeToSeriesMap = new HashMap<>();
@Subscribe
public void handleRefreshRequested(RefreshRequestedEvent event) {
update();
@ -477,17 +484,8 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
}
}
/**
* NOTE: Because this method modifies data directly used by the chart, this
* method should only be called from JavaFX thread!
*
* @param et the EventType to get the series for
*
* @return a Series object to contain all the events with the given
* EventType
*/
protected final XYChart.Series<X, Y> getSeries(final EventType et) {
return eventTypeToSeriesMap.get(et);
protected Interval getTimeRange() {
return filteredEvents.timeRangeProperty().get();
}
abstract protected class VisualizationUpdateTask<AxisValuesType> extends LoggedTask<Boolean> {
@ -496,30 +494,36 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
super(taskName, logStateChanges);
}
protected void installMaskerPane() {
MaskerPane maskerPane = new MaskerPane();
maskerPane.textProperty().bind(messageProperty());
maskerPane.progressProperty().bind(progressProperty());
setCenter(new StackPane(chart, maskerPane));
}
protected Interval getTimeRange() {
return filteredEvents.timeRangeProperty().get();
}
/**
* Sets initial progress value and message and shows blocking progress
* indicator over the visualization. Derived Tasks should be sure to
* call this as part of their call() implementation.
*
* @return true
*
* @throws Exception
*/
@NbBundle.Messages({"VisualizationUpdateTask.preparing=Analyzing zoom and filter settings"})
@Override
protected Boolean call() throws Exception {
updateProgress(-1, 1);
updateMessage(Bundle.VisualizationUpdateTask_preparing());
Platform.runLater(() -> {
installMaskerPane();
MaskerPane maskerPane = new MaskerPane();
maskerPane.textProperty().bind(messageProperty());
maskerPane.progressProperty().bind(progressProperty());
setCenter(new StackPane(chart, maskerPane));
setCursor(Cursor.WAIT);
});
return true;
}
/**
* updates the horisontal axis and removes the blocking progress
* indicator. Derived Tasks should be sure to call this as part of their
* succeeded() implementation.
*/
@Override
protected void succeeded() {
super.succeeded();
@ -532,7 +536,8 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
}
/**
* for use within the derived impementation of {@link #call() }
* Clears the chart data and sets the horisontal axis range. For use
* within the derived impementation of {@link #call() }
*
* @param axisValues
*/

View File

@ -252,20 +252,30 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
chart.setRangeInfo(rangeInfo); //do we need this. It seems like a hack.
List<Interval> intervals = rangeInfo.getIntervals();
//clear old data, and reset ranges and series
List<String> categories = Lists.transform(intervals, rangeInfo::formatForTick);
//clear old data, and reset ranges and series
resetChart(categories);
updateMessage(Bundle.CountsViewPane_loggedTask_updatingCounts());
int chartMax = 0;
int numIntervals = intervals.size();
/*
* for each interval query database for event counts and add to
* chart.
*
* Doing this in chunks might seem inefficient but it lets us reuse
* more cached results as the user navigates to overlapping viewws
*
* //TODO: implement similar chunked caching in DetailsView -jm
*/
for (int i = 0; i < numIntervals; i++) {
if (isCancelled()) {
return null;
}
updateProgress(i, numIntervals);
final Interval interval = intervals.get(i);
int maxPerInterval = 0; //used in total max tracking
int maxPerInterval = 0;
//query for current interval
Map<EventType, Long> eventCounts = filteredEvents.getEventCounts(interval);
@ -290,6 +300,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
}
chartMax = Math.max(chartMax, maxPerInterval);
}
//adjust vertical axis according to scale type and max counts
double countAxisUpperbound = 1 + chartMax * 1.2;
double tickUnit = ScaleType.LINEAR.equals(scale.get())
? Math.pow(10, Math.max(0, Math.floor(Math.log10(chartMax)) - 1))
@ -298,7 +309,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
countAxis.setTickUnit(tickUnit);
countAxis.setUpperBound(countAxisUpperbound);
});
return chartMax > 0;
return chartMax > 0; // are there events
}
@Override

View File

@ -173,6 +173,48 @@ final class EventCountsChart extends StackedBarChart<String, Number> implements
return SELECTED_NODE_EFFECT;
}
/**
* Add the bar click handler,tooltip, border styling and hover effect to the
* node generated by StackedBarChart.
*
* @param series
* @param itemIndex
* @param item
*/
@NbBundle.Messages({
"# {0} - count",
"# {1} - event type displayname",
"# {2} - start date time",
"# {3} - end date time",
"CountsViewPane.tooltip.text={0} {1} events\nbetween {2}\nand {3}"})
@Override
protected void dataItemAdded(Series<String, Number> series, int itemIndex, Data<String, Number> item) {
ExtraData extraValue = (ExtraData) item.getExtraValue();
EventType eventType = extraValue.getEventType();
Interval interval = extraValue.getInterval();
long count = extraValue.getRawCount();
item.nodeProperty().addListener((Observable o) -> {
final Node node = item.getNode();
if (node != null) {
node.setStyle("-fx-border-width: 2; -fx-border-color: " + ColorUtilities.getRGBCode(eventType.getSuperType().getColor()) + "; -fx-bar-fill: " + ColorUtilities.getRGBCode(eventType.getColor())); // NON-NLS
node.setCursor(Cursor.HAND);
final Tooltip tooltip = new Tooltip(Bundle.CountsViewPane_tooltip_text(
count, eventType.getDisplayName(),
item.getXValue(),
interval.getEnd().toString(rangeInfo.getTickFormatter())));
tooltip.setGraphic(new ImageView(eventType.getFXImage()));
Tooltip.install(node, tooltip);
node.setOnMouseEntered((mouseEntered) -> node.setEffect(new DropShadow(10, eventType.getColor())));
node.setOnMouseExited((MouseEvent mouseExited) -> node.setEffect(selectedNodes.contains(node) ? SELECTED_NODE_EFFECT : null));
node.setOnMouseClicked(new BarClickHandler(item));
}
});
super.dataItemAdded(series, itemIndex, item); //To change body of generated methods, choose Tools | Templates.
}
/**
* StringConvereter used to 'format' vertical axis labels
*/
@ -228,40 +270,6 @@ final class EventCountsChart extends StackedBarChart<String, Number> implements
}
}
@NbBundle.Messages({
"# {0} - count",
"# {1} - event type displayname",
"# {2} - start date time",
"# {3} - end date time",
"CountsViewPane.tooltip.text={0} {1} events\nbetween {2}\nand {3}"})
@Override
protected void dataItemAdded(Series<String, Number> series, int itemIndex, Data<String, Number> item) {
ExtraData extraValue = (ExtraData) item.getExtraValue();
EventType eventType = extraValue.getEventType();
Interval interval = extraValue.getInterval();
long count = extraValue.getRawCount();
item.nodeProperty().addListener((Observable o) -> {
final Node node = item.getNode();
if (node != null) {
node.setStyle("-fx-border-width: 2; -fx-border-color: " + ColorUtilities.getRGBCode(eventType.getSuperType().getColor()) + "; -fx-bar-fill: " + ColorUtilities.getRGBCode(eventType.getColor())); // NON-NLS
node.setCursor(Cursor.HAND);
final Tooltip tooltip = new Tooltip(Bundle.CountsViewPane_tooltip_text(
count, eventType.getDisplayName(),
item.getXValue(),
interval.getEnd().toString(rangeInfo.getTickFormatter())));
tooltip.setGraphic(new ImageView(eventType.getFXImage()));
Tooltip.install(node, tooltip);
node.setOnMouseEntered((mouseEntered) -> node.setEffect(new DropShadow(10, eventType.getColor())));
node.setOnMouseExited((MouseEvent mouseExited) -> node.setEffect(selectedNodes.contains(node) ? SELECTED_NODE_EFFECT : null));
node.setOnMouseClicked(new BarClickHandler(item));
}
});
super.dataItemAdded(series, itemIndex, item); //To change body of generated methods, choose Tools | Templates.
}
/**
* EventHandler for click events on nodes representing a bar(segment) in the
* stacked bar chart.
@ -415,6 +423,10 @@ final class EventCountsChart extends StackedBarChart<String, Number> implements
}
}
/**
* Encapsulate extra data stuffed into each {@link Data} item to give click
* handler and tooltip access to more info.
*/
static class ExtraData {
private final Interval interval;

View File

@ -110,7 +110,9 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
private ContextMenu chartContextMenu;
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)//at start of layout pass
private Set<String> activeQuickHidefilters;
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)//at start of layout pass
private double descriptionWidth;
@Override
@ -420,12 +422,15 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
protected void layoutPlotChildren() {
setCursor(Cursor.WAIT);
maxY.set(0);
//These don't change during a layout pass and are expensive to compute per node. So we do it once at the start
activeQuickHidefilters = getController().getQuickHideFilters().stream()
.filter(AbstractFilter::isActive)
.map(DescriptionFilter::getDescription)
.collect(Collectors.toSet());
descriptionWidth = getDescriptionWidth();
//This dosn't change during a layout pass and is expensive to compute per node. So we do it once at the start
descriptionWidth = truncateAll.get() ? truncateWidth.get() : USE_PREF_SIZE;
if (bandByType.get()) {
sortedStripeNodes.stream()
@ -438,10 +443,6 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
setCursor(null);
}
private double getDescriptionWidth() {
return truncateAll.get() ? truncateWidth.get() : USE_PREF_SIZE;
}
ReadOnlyDoubleProperty maxVScrollProperty() {
return maxY.getReadOnlyProperty();
}
@ -531,8 +532,8 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
//initial test position
double yTop = (oneEventPerRow.get())
? (localMax + MINIMUM_EVENT_NODE_GAP)
: computeYTop(minY, h, maxXatY, xLeft, xRight); // if onePerRow, just put it at end
? (localMax + MINIMUM_EVENT_NODE_GAP)// if onePerRow, just put it at end
: computeYTop(minY, h, maxXatY, xLeft, xRight);
localMax = Math.max(yTop + h, localMax);
@ -545,6 +546,23 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
return localMax; //return new max
}
/**
* Given information about the current layout pass so far and about a
* particular node, compute the y position of that node.
*
*
* @param yMin the smallest (towards the top of the screen) y position to
* consider
* @param h the height of the node we are trying to position
* @param maxXatY a map from y ranges to the max x within that range. NOTE:
* This map will be updated to include the node in question.
* @param xLeft the left x-cord of the node to position
* @param xRight the left x-cord of the node to position
*
* @return the y position for the node in question.
*
*
*/
private double computeYTop(double yMin, double h, TreeRangeMap<Double, Double> maxXatY, double xLeft, double xRight) {
double yTop = yMin;
double yBottom = yTop + h;
@ -589,10 +607,10 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
}
/**
* expose as public
* expose as protected
*/
@Override
public void requestChartLayout() {
protected void requestChartLayout() {
super.requestChartLayout();
}

View File

@ -1,7 +1,20 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.ui.detailview;
@ -14,10 +27,6 @@ import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
* More specifically it takes an EventStripeNode and produces a stream of
* EventStripes containing the stripes for the given node and all child
* eventStripes, ignoring intervening EventCluster nodes.
*
* @see
* #loadSubBundles(org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD.RelativeDetail)
* for usage
*/
class StripeFlattener implements Function<EventStripeNode, Stream<EventStripe>> {