finish converting image analyzer to use History.java

This commit is contained in:
jmillman 2014-08-28 10:21:18 -04:00
parent ddb9d0c245
commit 9012e245f0
4 changed files with 116 additions and 316 deletions

View File

@ -37,7 +37,7 @@ import javax.annotation.concurrent.ThreadSafe;
* current/historical/future states * current/historical/future states
*/ */
@ThreadSafe @ThreadSafe
public class HistoryManager<T> { public class History<T> {
@GuardedBy("this") @GuardedBy("this")
private final ObservableStack<T> historyStack = new ObservableStack<>(); private final ObservableStack<T> historyStack = new ObservableStack<>();
@ -78,12 +78,12 @@ public class HistoryManager<T> {
return canRetreat.getReadOnlyProperty(); return canRetreat.getReadOnlyProperty();
} }
public HistoryManager(T initialState) { public History(T initialState) {
this(); this();
currentState.set(initialState); currentState.set(initialState);
} }
public HistoryManager() { public History() {
canAdvance.bind(forwardStack.emptyProperty().not()); canAdvance.bind(forwardStack.emptyProperty().not());
canRetreat.bind(historyStack.emptyProperty().not()); canRetreat.bind(historyStack.emptyProperty().not());
} }
@ -112,15 +112,15 @@ public class HistoryManager<T> {
* @return the state retreated to, or null if there were no history states. * @return the state retreated to, or null if there were no history states.
*/ */
synchronized public T retreat() { synchronized public T retreat() {
final T peek = historyStack.pop(); final T pop = historyStack.pop();
if (peek != null && peek.equals(currentState.get()) == false) { if (pop != null && pop.equals(currentState.get()) == false) {
forwardStack.push(currentState.get()); forwardStack.push(currentState.get());
currentState.set(peek); currentState.set(pop);
} else if (peek != null && peek.equals(currentState.get())) { } else if (pop != null && pop.equals(currentState.get())) {
return retreat(); return retreat();
} }
return peek; return pop;
} }
/** /**
@ -129,12 +129,8 @@ public class HistoryManager<T> {
* by invoking the equals method. Throws away any forward states. * by invoking the equals method. Throws away any forward states.
* *
* @param newState the new state to advance to * @param newState the new state to advance to
* @throws IllegalArgumentException if the newState is null
*/ */
synchronized public void advance(T newState) throws IllegalArgumentException { synchronized public void advance(T newState) throws IllegalArgumentException {
if (newState == null) {
throw new IllegalArgumentException("newState must be non-null");
}
if (currentState.equals(newState) == false) { if (currentState.equals(newState) == false) {
if (currentState.get() != null) { if (currentState.get() != null) {
historyStack.push(currentState.get()); historyStack.push(currentState.get());
@ -149,7 +145,9 @@ public class HistoryManager<T> {
} }
public void clear() { public void clear() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. historyStack.clear();
forwardStack.clear();
currentState.set(null);
} }
/** /**

View File

@ -1,196 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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;
import javafx.beans.property.Property;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
/**
* A basic history implementation. Keeps a history (and forward) stack of state
* objects of type <T>. exposes current state and availability of
* advance/retreat operations via methods and JFX {@link Property}s
*
* @param <T> the type of objects used to represent the
* current/historical/future states
*/
@ThreadSafe
public class HistoryManager<T> {
@GuardedBy("this")
private final ObservableStack<T> historyStack = new ObservableStack<>();
@GuardedBy("this")
private final ObservableStack<T> forwardStack = new ObservableStack<>();
@GuardedBy("this")
private final ReadOnlyObjectWrapper<T> currentState = new ReadOnlyObjectWrapper<>();
@GuardedBy("this")
private final ReadOnlyBooleanWrapper canAdvance = new ReadOnlyBooleanWrapper();
@GuardedBy("this")
private final ReadOnlyBooleanWrapper canRetreat = new ReadOnlyBooleanWrapper();
synchronized public T getCurrentState() {
return currentState.get();
}
synchronized public boolean canAdvance() {
return canAdvance.get();
}
synchronized public boolean canRetreat() {
return canRetreat.get();
}
synchronized public ReadOnlyObjectProperty<T> currentState() {
return currentState.getReadOnlyProperty();
}
synchronized public ReadOnlyBooleanProperty getCanAdvance() {
return canAdvance.getReadOnlyProperty();
}
synchronized public ReadOnlyBooleanProperty getCanRetreat() {
return canRetreat.getReadOnlyProperty();
}
public HistoryManager(T initialState) {
this();
currentState.set(initialState);
}
public HistoryManager() {
canAdvance.bind(forwardStack.emptyProperty().not());
canRetreat.bind(historyStack.emptyProperty().not());
}
/**
* advance through the forward states by one, and put the current state in
* the history. Is a no-op if there are no forward states.
*
* @return the state advanced to, or null if there were no forward states.
*/
synchronized public T advance() {
final T peek = forwardStack.peek();
if (peek != null && peek.equals(currentState.get()) == false) {
historyStack.push(currentState.get());
currentState.set(peek);
forwardStack.pop();
}
return peek;
}
/**
* retreat through the history states by one, and add the current state to
* the forward states. Is a no-op if there are no history states.
*
* @return the state retreated to, or null if there were no history states.
*/
synchronized public T retreat() {
final T peek = historyStack.pop();
if (peek != null && peek.equals(currentState.get()) == false) {
forwardStack.push(currentState.get());
currentState.set(peek);
} else if (peek != null && peek.equals(currentState.get())) {
return retreat();
}
return peek;
}
/**
* put the current state in the history and advance to the given state. It
* is a no-op if the current state is equal to the given state as determined
* by invoking the equals method. Throws away any forward states.
*
* @param newState the new state to advance to
* @throws IllegalArgumentException if the newState is null
*/
synchronized public void advance(T newState) throws IllegalArgumentException {
if (newState == null) {
throw new IllegalArgumentException("newState must be non-null");
}
if (currentState.equals(newState) == false) {
if (currentState.get() != null) {
historyStack.push(currentState.get());
}
currentState.set(newState);
if (newState.equals(forwardStack.peek())) {
forwardStack.pop();
} else {
forwardStack.clear();
}
}
}
public void clear() {
historyStack.clear();
forwardStack.clear();
currentState.set(null);
}
/**
* A simple extension to SimpleListProperty to add a stack api
*
* TODO: this really should not extend SimpleListProperty but should
* delegate to an appropriate observable implementation while implementing
* the {@link Deque} interface
*/
private static class ObservableStack<T> extends SimpleListProperty<T> {
public ObservableStack() {
super(FXCollections.<T>synchronizedObservableList(FXCollections.<T>observableArrayList()));
}
public void push(T item) {
synchronized (this) {
add(0, item);
}
}
public T pop() {
synchronized (this) {
if (isEmpty()) {
return null;
} else {
return remove(0);
}
}
}
public T peek() {
synchronized (this) {
if (isEmpty()) {
return null;
} else {
return get(0);
}
}
}
}
}

View File

@ -62,6 +62,7 @@ import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
import static org.sleuthkit.autopsy.casemodule.Case.Events.DATA_SOURCE_ADDED; import static org.sleuthkit.autopsy.casemodule.Case.Events.DATA_SOURCE_ADDED;
import org.sleuthkit.autopsy.coreutils.History;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
@ -170,7 +171,7 @@ public class TimeLineController {
private final ZoomParams InitialZoomState; private final ZoomParams InitialZoomState;
@GuardedBy("this") @GuardedBy("this")
private final HistoryManager<ZoomParams> historyManager = new HistoryManager<>(); private final History<ZoomParams> historyManager = new History<>();
//all members should be access with the intrinsict lock of this object held //all members should be access with the intrinsict lock of this object held
//selected events (ie shown in the result viewer) //selected events (ie shown in the result viewer)

View File

@ -63,6 +63,7 @@ import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory; import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.util.Exceptions; import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.History;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.imageanalyzer.datamodel.Category; import org.sleuthkit.autopsy.imageanalyzer.datamodel.Category;
import org.sleuthkit.autopsy.imageanalyzer.datamodel.DrawableAttribute; import org.sleuthkit.autopsy.imageanalyzer.datamodel.DrawableAttribute;
@ -77,8 +78,6 @@ import org.sleuthkit.autopsy.imageanalyzer.gui.NoGroupsDialog;
import org.sleuthkit.autopsy.imageanalyzer.gui.SummaryTablePane; import org.sleuthkit.autopsy.imageanalyzer.gui.SummaryTablePane;
import org.sleuthkit.autopsy.imageanalyzer.progress.ProgressAdapterBase; import org.sleuthkit.autopsy.imageanalyzer.progress.ProgressAdapterBase;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.timeline.HistoryManager;
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -113,7 +112,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
} }
@GuardedBy("this") @GuardedBy("this")
private final HistoryManager<GroupViewState> historyManager = new HistoryManager<>(); private final History<GroupViewState> historyManager = new History<>();
private final ReadOnlyBooleanWrapper listeningEnabled = new ReadOnlyBooleanWrapper(false); private final ReadOnlyBooleanWrapper listeningEnabled = new ReadOnlyBooleanWrapper(false);
@ -155,9 +154,6 @@ public class ImageAnalyzerController implements FileUpdateListener {
return regroupDisabled.getReadOnlyProperty(); return regroupDisabled.getReadOnlyProperty();
} }
public ReadOnlyObjectProperty<GroupViewState> viewState() { public ReadOnlyObjectProperty<GroupViewState> viewState() {
return historyManager.currentState(); return historyManager.currentState();
} }
@ -225,9 +221,9 @@ public class ImageAnalyzerController implements FileUpdateListener {
}); });
groupManager.getUnSeenGroups().addListener((Observable observable) -> { groupManager.getUnSeenGroups().addListener((Observable observable) -> {
//if there are unseen groups and none being viewed
if (groupManager.getUnSeenGroups().size() > 0 && (getViewState() == null || getViewState().getGroup() == null)) { if (groupManager.getUnSeenGroups().size() > 0 && (getViewState() == null || getViewState().getGroup() == null)) {
advance(GroupViewState.tile(groupManager.getUnSeenGroups().get(0)));
setViewState(GroupViewState.tile(groupManager.getUnSeenGroups().get(0)));
} }
}); });
regroupDisabled.addListener((Observable observable) -> { regroupDisabled.addListener((Observable observable) -> {
@ -286,8 +282,10 @@ public class ImageAnalyzerController implements FileUpdateListener {
return historyManager.getCanRetreat(); return historyManager.getCanRetreat();
} }
synchronized public void advance(GroupViewState viewState) { synchronized public void advance(GroupViewState newState) {
historyManager.advance(viewState); if (viewState().get() == null ||( viewState().get().getGroup() != newState.getGroup())) {
historyManager.advance(newState);
}
} }
synchronized public GroupViewState advance() { synchronized public GroupViewState advance() {
@ -309,7 +307,7 @@ public class ImageAnalyzerController implements FileUpdateListener {
*/ */
public final void checkForGroups() { public final void checkForGroups() {
if (groupManager.getAnalyzedGroups().isEmpty()) { if (groupManager.getAnalyzedGroups().isEmpty()) {
setViewState(null); advance(null);
if (IngestManager.getInstance().isIngestRunning()) { if (IngestManager.getInstance().isIngestRunning()) {
if (listeningEnabled.get() == false) { if (listeningEnabled.get() == false) {
replaceNotification(fullUIStackPane, replaceNotification(fullUIStackPane,
@ -474,7 +472,6 @@ public class ImageAnalyzerController implements FileUpdateListener {
LOGGER.info("resetting EurekaControler to initial state."); LOGGER.info("resetting EurekaControler to initial state.");
selectionModel.clearSelection(); selectionModel.clearSelection();
Platform.runLater(() -> { Platform.runLater(() -> {
historyManager.clear(); historyManager.clear();
}); });