Merge branch 'develop' of https://github.com/sleuthkit/autopsy into vm_detection

This commit is contained in:
Eugene Livis 2016-01-19 14:40:34 -05:00
commit d3632847d0
11 changed files with 152 additions and 119 deletions

View File

@ -234,22 +234,29 @@ public class ImageUtils {
*/ */
public static boolean isGIF(AbstractFile file) { public static boolean isGIF(AbstractFile file) {
try { try {
final FileTypeDetector fileTypeDetector = getFileTypeDetector(); final FileTypeDetector myFileTypeDetector = getFileTypeDetector();
if (nonNull(fileTypeDetector)) { if (nonNull(myFileTypeDetector)) {
String fileType = fileTypeDetector.getFileType(file); String fileType = myFileTypeDetector.getFileType(file);
return IMAGE_GIF_MIME.equalsIgnoreCase(fileType); return IMAGE_GIF_MIME.equalsIgnoreCase(fileType);
} }
} catch (TskCoreException | FileTypeDetectorInitException ex) { } catch (FileTypeDetectorInitException ex) {
LOGGER.log(Level.WARNING, "Failed to get mime type with FileTypeDetector.", ex); //NOI18N LOGGER.log(Level.WARNING, "Failed to initialize FileTypeDetector.", ex); //NOI18N
} catch (TskCoreException ex) {
if (ex.getMessage().contains("An SQLException was provoked by the following failure: java.lang.InterruptedException")) {
LOGGER.log(Level.WARNING, "Mime type look up with FileTypeDetector was interupted."); //NOI18N}
return "gif".equalsIgnoreCase(file.getNameExtension()); //NOI18N
} else {
LOGGER.log(Level.SEVERE, "Failed to get mime type of " + getContentPathSafe(file) + " with FileTypeDetector.", ex); //NOI18N}
}
} }
LOGGER.log(Level.WARNING, "Falling back on direct mime type check."); //NOI18N LOGGER.log(Level.WARNING, "Falling back on direct mime type check for {0}.", getContentPathSafe(file)); //NOI18N
switch (file.isMimeType(GIF_MIME_SET)) { switch (file.isMimeType(GIF_MIME_SET)) {
case TRUE: case TRUE:
return true; return true;
case UNDEFINED: case UNDEFINED:
LOGGER.log(Level.WARNING, "Falling back on extension check."); //NOI18N LOGGER.log(Level.WARNING, "Falling back on extension check."); //NOI18N
return "gif".equals(file.getNameExtension()); //NOI18N return "gif".equalsIgnoreCase(file.getNameExtension()); //NOI18N
case FALSE: case FALSE:
default: default:
return false; return false;
@ -660,6 +667,9 @@ public class ImageUtils {
if (isGIF(file)) { if (isGIF(file)) {
return readImage(); return readImage();
} }
if (isCancelled()) {
return null;
}
// If a thumbnail file is already saved locally, just read that. // If a thumbnail file is already saved locally, just read that.
if (cacheFile != null && cacheFile.exists()) { if (cacheFile != null && cacheFile.exists()) {
@ -673,6 +683,9 @@ public class ImageUtils {
} }
} }
if (isCancelled()) {
return null;
}
//There was no correctly-sized cached thumbnail so make one. //There was no correctly-sized cached thumbnail so make one.
BufferedImage thumbnail = null; BufferedImage thumbnail = null;
@ -722,6 +735,11 @@ public class ImageUtils {
throw e; throw e;
} }
} }
if (isCancelled()) {
return null;
}
updateProgress(-1, 1); updateProgress(-1, 1);
//if we got a valid thumbnail save it //if we got a valid thumbnail save it
@ -810,7 +828,9 @@ public class ImageUtils {
} }
//fall through to default image reading code if there was an error //fall through to default image reading code if there was an error
} }
if (isCancelled()) {
return null;
}
try (ImageInputStream input = ImageIO.createImageInputStream(inputStream)) { try (ImageInputStream input = ImageIO.createImageInputStream(inputStream)) {
if (input == null) { if (input == null) {
throw new IIOException(COULD_NOT_CREATE_IMAGE_INPUT_STREAM); throw new IIOException(COULD_NOT_CREATE_IMAGE_INPUT_STREAM);
@ -834,9 +854,6 @@ public class ImageUtils {
param.setDestination(bufferedImage); param.setDestination(bufferedImage);
try { try {
bufferedImage = reader.read(0, param); //should always be same bufferedImage object bufferedImage = reader.read(0, param); //should always be same bufferedImage object
if (isCancelled()) {
return null;
}
} catch (IOException iOException) { } catch (IOException iOException) {
// Ignore this exception or display a warning or similar, for exceptions happening during decoding // Ignore this exception or display a warning or similar, for exceptions happening during decoding
LOGGER.log(Level.WARNING, IMAGE_UTILS_COULD_NOT_READ_UNSUPPORTE_OR_CORRUPT + ": " + iOException.toString(), ImageUtils.getContentPathSafe(file)); //NOI18N LOGGER.log(Level.WARNING, IMAGE_UTILS_COULD_NOT_READ_UNSUPPORTE_OR_CORRUPT + ": " + iOException.toString(), ImageUtils.getContentPathSafe(file)); //NOI18N
@ -844,6 +861,9 @@ public class ImageUtils {
reader.removeIIOReadProgressListener(this); reader.removeIIOReadProgressListener(this);
reader.dispose(); reader.dispose();
} }
if (isCancelled()) {
return null;
}
return SwingFXUtils.toFXImage(bufferedImage, null); return SwingFXUtils.toFXImage(bufferedImage, null);
} else { } else {
throw new IIOException(NO_IMAGE_READER_FOUND_FOR_ + ImageUtils.getContentPathSafe(file)); throw new IIOException(NO_IMAGE_READER_FOUND_FOR_ + ImageUtils.getContentPathSafe(file));
@ -857,6 +877,7 @@ public class ImageUtils {
//update this task with the progress reported by ImageReader.read //update this task with the progress reported by ImageReader.read
updateProgress(percentageDone, 100); updateProgress(percentageDone, 100);
if (isCancelled()) { if (isCancelled()) {
reader.removeIIOReadProgressListener(this);
reader.abort(); reader.abort();
reader.dispose(); reader.dispose();
} }

View File

@ -60,12 +60,19 @@ public interface IngestModule {
*/ */
public class IngestModuleException extends Exception { public class IngestModuleException extends Exception {
private static final long serialVersionUID = 1L;
@Deprecated
public IngestModuleException() { public IngestModuleException() {
} }
public IngestModuleException(String message) { public IngestModuleException(String message) {
super(message); super(message);
} }
public IngestModuleException(String message, Throwable cause) {
super(message, cause);
}
} }
/** /**

View File

@ -119,8 +119,8 @@ class CallLogAnalyzer {
} else { /// Covers INCOMING and MISSED } else { /// Covers INCOMING and MISSED
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM.getTypeID(), moduleName, number)); bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM.getTypeID(), moduleName, number));
} }
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(), moduleName, date)); bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(), moduleName, date)); // RC: Should be long!
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID(), moduleName, duration + date)); bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID(), moduleName, duration + date)); // RC: Should be long!
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(), moduleName, type)); bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(), moduleName, type));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, name)); bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, name));

View File

@ -24,6 +24,8 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -89,7 +91,14 @@ import org.sleuthkit.datamodel.TskData;
* Connects different parts of ImageGallery together and is hub for flow of * Connects different parts of ImageGallery together and is hub for flow of
* control. * control.
*/ */
public final class ImageGalleryController { public final class ImageGalleryController implements Executor {
private final Executor execDelegate = Executors.newSingleThreadExecutor();
@Override
public void execute(Runnable command) {
execDelegate.execute(command);
}
private static final Logger LOGGER = Logger.getLogger(ImageGalleryController.class.getName()); private static final Logger LOGGER = Logger.getLogger(ImageGalleryController.class.getName());
@ -838,7 +847,7 @@ public final class ImageGalleryController {
/** /**
* Copy files from a newly added data source into the DB. Get all * Copy files from a newly added data source into the DB. Get all
* "drawable" files, based on extension. After ingest we use file type * "drawable" files, based on extension. After ingest we use file type
* id module and if necessary jpeg signature matching to add/remove * id module and if necessary jpeg/png signature matching to add/remove
* files * files
*/ */
@Override @Override
@ -847,30 +856,28 @@ public final class ImageGalleryController {
updateMessage("prepopulating image/video database"); updateMessage("prepopulating image/video database");
try { try {
String fsQuery = ""; String fsQuery = "(fs_obj_id IS NULL) "; //default clause
/*
* NOTE: Logical files currently (Apr '15) have a null value for
* fs_obj_id in DB. for them, we will not specify a fs_obj_id,
* which means we will grab files from another data source, but
* the drawable DB is smart enough to de-dupe them. For Images
* we can do better.
*/
if (dataSource instanceof Image) { if (dataSource instanceof Image) {
List<FileSystem> fileSystems = ((Image) dataSource).getFileSystems(); List<FileSystem> fileSystems = ((Image) dataSource).getFileSystems();
if (fileSystems.isEmpty() == false) { if (fileSystems.isEmpty()) {
/* /*
* no filesystems, don't bother with the initial * no filesystems, don't bother with the initial
* population, just catch things on file_done * population, just sort things out on file_done events
*/ */
progressHandle.finish(); progressHandle.finish();
return; return;
} }
String internal = fileSystems.stream() //use this clause to only grab files from the newly added filesystems.
fsQuery = fileSystems.stream()
.map(fileSystem -> String.valueOf(fileSystem.getId())) .map(fileSystem -> String.valueOf(fileSystem.getId()))
.collect(Collectors.joining(" OR fs_obj_id = ")); .collect(Collectors.joining(" OR fs_obj_id = ", "(fs_obj_id = ", ") "));
fsQuery = "(fs_obj_id = " + internal + ") "; //suffix
} else {
/*
* NOTE: Logical files currently (Apr '15) have a null value
* for fs_obj_id in DB. for them, we will not specify a
* fs_obj_id, which means we will grab files from another
* data source, but the drawable DB is smart enough to
* de-dupe them.
*/
fsQuery = "(fs_obj_id IS NULL) ";
} }
final List<AbstractFile> files = getSleuthKitCase().findAllFilesWhere(fsQuery + " AND " + DRAWABLE_QUERY); final List<AbstractFile> files = getSleuthKitCase().findAllFilesWhere(fsQuery + " AND " + DRAWABLE_QUERY);

View File

@ -39,11 +39,11 @@ import javafx.scene.image.Image;
import javafx.scene.image.WritableImage; import javafx.scene.image.WritableImage;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
import org.sleuthkit.autopsy.imagegallery.gui.Toolbar; import org.sleuthkit.autopsy.imagegallery.gui.Toolbar;
import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils;
import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -184,12 +184,7 @@ public enum ThumbnailCache {
public Task<Image> getThumbnailTask(DrawableFile<?> file) { public Task<Image> getThumbnailTask(DrawableFile<?> file) {
final Image thumbnail = cache.getIfPresent(file.getId()); final Image thumbnail = cache.getIfPresent(file.getId());
if (thumbnail != null) { if (thumbnail != null) {
return new Task<Image>() { return TaskUtils.taskFrom(() -> thumbnail);
@Override
protected Image call() throws Exception {
return thumbnail;
}
};
} }
final Task<Image> newGetThumbnailTask = ImageUtils.newGetThumbnailTask(file.getAbstractFile(), MAX_THUMBNAIL_SIZE, false); final Task<Image> newGetThumbnailTask = ImageUtils.newGetThumbnailTask(file.getAbstractFile(), MAX_THUMBNAIL_SIZE, false);
newGetThumbnailTask.stateProperty().addListener((Observable observable) -> { newGetThumbnailTask.stateProperty().addListener((Observable observable) -> {
@ -198,7 +193,7 @@ public enum ThumbnailCache {
try { try {
cache.put(Long.MIN_VALUE, newGetThumbnailTask.get()); cache.put(Long.MIN_VALUE, newGetThumbnailTask.get());
} catch (InterruptedException | ExecutionException ex) { } catch (InterruptedException | ExecutionException ex) {
Exceptions.printStackTrace(ex); LOGGER.log(Level.SEVERE, "There was an exception even though thumbnail task succedded for. This should not be possible.", ex);
} }
} }
}); });

View File

@ -19,9 +19,9 @@
package org.sleuthkit.autopsy.imagegallery.actions; package org.sleuthkit.autopsy.imagegallery.actions;
import java.util.Optional; import java.util.Optional;
import javafx.application.Platform;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.beans.binding.ObjectExpression; import javafx.beans.binding.ObjectExpression;
import javafx.concurrent.Task;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
@ -36,10 +36,10 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
*/ */
public class NextUnseenGroup extends Action { public class NextUnseenGroup extends Action {
private static final Image END private static final Image END =
= new Image(NextUnseenGroup.class.getResourceAsStream("/org/sleuthkit/autopsy/imagegallery/images/control-stop.png")); new Image(NextUnseenGroup.class.getResourceAsStream("/org/sleuthkit/autopsy/imagegallery/images/control-stop.png"));
private static final Image ADVANCE private static final Image ADVANCE =
= new Image(NextUnseenGroup.class.getResourceAsStream("/org/sleuthkit/autopsy/imagegallery/images/control-double.png")); new Image(NextUnseenGroup.class.getResourceAsStream("/org/sleuthkit/autopsy/imagegallery/images/control-double.png"));
private static final String MARK_GROUP_SEEN = "Mark Group Seen"; private static final String MARK_GROUP_SEEN = "Mark Group Seen";
private static final String NEXT_UNSEEN_GROUP = "Next Unseen group"; private static final String NEXT_UNSEEN_GROUP = "Next Unseen group";
@ -53,25 +53,36 @@ public class NextUnseenGroup extends Action {
//TODO: do we need both these listeners? //TODO: do we need both these listeners?
controller.getGroupManager().getAnalyzedGroups().addListener((Observable observable) -> { controller.getGroupManager().getAnalyzedGroups().addListener((Observable observable) -> {
Platform.runLater(this::updateButton); updateButton();
}); });
controller.getGroupManager().getUnSeenGroups().addListener((Observable observable) -> { controller.getGroupManager().getUnSeenGroups().addListener((Observable observable) -> {
Platform.runLater(this::updateButton); updateButton();
}); });
setEventHandler((ActionEvent t) -> { setEventHandler((ActionEvent t) -> {
//fx-thread
//if there is a group assigned to the view, mark it as seen //if there is a group assigned to the view, mark it as seen
Optional.ofNullable(controller.viewState()) Optional.ofNullable(controller.viewState())
.map(ObjectExpression<GroupViewState>::getValue) .map(ObjectExpression<GroupViewState>::getValue)
.map(GroupViewState::getGroup) .map(GroupViewState::getGroup)
.ifPresent(group -> controller.getGroupManager().markGroupSeen(group, true)); .ifPresent(group -> controller.getGroupManager().markGroupSeen(group, true));
controller.execute(new Task<Void>() {
if (false == controller.getGroupManager().getUnSeenGroups().isEmpty()) { @Override
controller.advance(GroupViewState.tile(controller.getGroupManager().getUnSeenGroups().get(0)), true); protected Void call() throws Exception {
} if (false == controller.getGroupManager().getUnSeenGroups().isEmpty()) {
updateButton(); controller.advance(GroupViewState.tile(controller.getGroupManager().getUnSeenGroups().get(0)), true);
}
return null;
}
@Override
protected void succeeded() {
super.succeeded();
updateButton();
}
});
}); });
updateButton(); updateButton();

View File

@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
import java.util.Objects; import java.util.Objects;
import java.util.logging.Level; import java.util.logging.Level;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.WeakChangeListener;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.CacheHint; import javafx.scene.CacheHint;
@ -48,6 +50,13 @@ public class DrawableTile extends DrawableTileBase {
private static final DropShadow LAST_SELECTED_EFFECT = new DropShadow(10, Color.BLUE); private static final DropShadow LAST_SELECTED_EFFECT = new DropShadow(10, Color.BLUE);
private static final Logger LOGGER = Logger.getLogger(DrawableTile.class.getName()); private static final Logger LOGGER = Logger.getLogger(DrawableTile.class.getName());
private final ChangeListener<? super Long> lastSelectionListener = (observable, oldValue, newValue) -> {
try {
setEffect(Objects.equals(newValue, getFileID()) ? LAST_SELECTED_EFFECT : null);
} catch (java.lang.IllegalStateException ex) {
Logger.getLogger(DrawableTile.class.getName()).log(Level.WARNING, "Error displaying tile");
}
};
@FXML @FXML
@Override @Override
@ -63,13 +72,7 @@ public class DrawableTile extends DrawableTileBase {
imageView.fitHeightProperty().bind(Toolbar.getDefault(getController()).sizeSliderValue()); imageView.fitHeightProperty().bind(Toolbar.getDefault(getController()).sizeSliderValue());
imageView.fitWidthProperty().bind(Toolbar.getDefault(getController()).sizeSliderValue()); imageView.fitWidthProperty().bind(Toolbar.getDefault(getController()).sizeSliderValue());
selectionModel.lastSelectedProperty().addListener((observable, oldValue, newValue) -> { selectionModel.lastSelectedProperty().addListener(new WeakChangeListener<>(lastSelectionListener));
try {
setEffect(Objects.equals(newValue, getFileID()) ? LAST_SELECTED_EFFECT : null);
} catch (java.lang.IllegalStateException ex) {
Logger.getLogger(DrawableTile.class.getName()).log(Level.WARNING, "Error displaying tile");
}
});
} }
public DrawableTile(GroupPane gp, ImageGalleryController controller) { public DrawableTile(GroupPane gp, ImageGalleryController controller) {
@ -100,6 +103,4 @@ public class DrawableTile extends DrawableTileBase {
return getFile().map(AbstractContent::getName).orElse(""); return getFile().map(AbstractContent::getName).orElse("");
} }
} }

View File

@ -25,7 +25,9 @@ import java.util.Collection;
import java.util.Optional; import java.util.Optional;
import java.util.logging.Level; import java.util.logging.Level;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.beans.WeakInvalidationListener;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import javafx.fxml.FXML; import javafx.fxml.FXML;
@ -111,7 +113,6 @@ public abstract class DrawableTileBase extends DrawableUIBase {
@FXML @FXML
private ImageView hashHitImageView; private ImageView hashHitImageView;
/** /**
* displays the icon representing follow up tag * displays the icon representing follow up tag
*/ */
@ -148,7 +149,7 @@ public abstract class DrawableTileBase extends DrawableUIBase {
super(controller); super(controller);
this.groupPane = groupPane; this.groupPane = groupPane;
selectionModel = controller.getSelectionModel(); selectionModel = controller.getSelectionModel();
selectionModel.getSelected().addListener((Observable observable) -> updateSelectionState()); selectionModel.getSelected().addListener(new WeakInvalidationListener(selectionListener));
//set up mouse listener //set up mouse listener
//TODO: split this between DrawableTile and SingleDrawableViewBase //TODO: split this between DrawableTile and SingleDrawableViewBase
@ -243,6 +244,7 @@ public abstract class DrawableTileBase extends DrawableUIBase {
} }
}); });
} }
private final InvalidationListener selectionListener = (Observable observable) -> updateSelectionState();
GroupPane getGroupPane() { GroupPane getGroupPane() {
return groupPane; return groupPane;

View File

@ -157,7 +157,11 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
} }
}); });
myTask.setOnCancelled(cancelled -> { myTask.setOnCancelled(cancelled -> {
disposeContent(); synchronized (DrawableUIBase.this) {
imageTask = null;
}
imageView.setImage(null);
imageBorder.setCenter(null);
}); });
exec.execute(myTask); exec.execute(myTask);

View File

@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.imagegallery.gui.navpanel;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
@ -108,32 +107,12 @@ final public class NavPanel extends TabPane {
sortByBox.setButtonCell(new TreeNodeComparators.ComparatorListCell()); sortByBox.setButtonCell(new TreeNodeComparators.ComparatorListCell());
sortByBox.setItems(FXCollections.observableArrayList(FXCollections.observableArrayList(TreeNodeComparators.values()))); sortByBox.setItems(FXCollections.observableArrayList(FXCollections.observableArrayList(TreeNodeComparators.values())));
sortByBox.getSelectionModel().select(TreeNodeComparators.HIT_COUNT); sortByBox.getSelectionModel().select(TreeNodeComparators.HIT_COUNT);
sortByBox.getSelectionModel().selectedItemProperty().addListener((Observable o) -> { sortByBox.getSelectionModel().selectedItemProperty().addListener(o -> resortHashTree());
//user action ->jfx thread
resortHashTree();
});
navTree.setRoot(navTreeRoot); configureTree(navTree, navTreeRoot);
navTreeRoot.setExpanded(true); configureTree(hashTree, hashTreeRoot);
navTree.setCellFactory((TreeView<TreeNode> p) -> new GroupTreeCell());
navTree.setShowRoot(false);
navTree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
hashTree.setRoot(hashTreeRoot);
hashTreeRoot.setExpanded(true);
hashTree.setCellFactory((TreeView<TreeNode> p) -> new GroupTreeCell());
hashTree.setShowRoot(false);
hashTree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
activeTreeProperty.addListener((Observable o) -> {
updateControllersGroup();
activeTreeProperty.get().getSelectionModel().selectedItemProperty().addListener((Observable o1) -> {
updateControllersGroup();
});
});
this.activeTreeProperty.set(navTree);
activeTreeProperty.set(navTree);
navTabPane.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends Tab> ov, Tab t, Tab t1) -> { navTabPane.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends Tab> ov, Tab t, Tab t1) -> {
if (t1 == hashTab) { if (t1 == hashTab) {
activeTreeProperty.set(hashTree); activeTreeProperty.set(hashTree);
@ -143,7 +122,6 @@ final public class NavPanel extends TabPane {
}); });
controller.getGroupManager().getAnalyzedGroups().addListener((ListChangeListener.Change<? extends DrawableGroup> change) -> { controller.getGroupManager().getAnalyzedGroups().addListener((ListChangeListener.Change<? extends DrawableGroup> change) -> {
while (change.next()) { while (change.next()) {
for (DrawableGroup g : change.getAddedSubList()) { for (DrawableGroup g : change.getAddedSubList()) {
insertIntoNavTree(g); insertIntoNavTree(g);
@ -158,15 +136,23 @@ final public class NavPanel extends TabPane {
} }
}); });
rebuildTrees();
controller.viewState().addListener((ObservableValue<? extends GroupViewState> observable, GroupViewState oldValue, GroupViewState newValue) -> { controller.viewState().addListener((ObservableValue<? extends GroupViewState> observable, GroupViewState oldValue, GroupViewState newValue) -> {
if (newValue != null && newValue.getGroup() != null) { if (newValue != null && newValue.getGroup() != null) {
Platform.runLater(() -> { setFocusedGroup(newValue.getGroup());
setFocusedGroup(newValue.getGroup());
});
} }
}); });
rebuildTrees();
}
@ThreadConfined(type = ThreadType.JFX)
private void configureTree(final TreeView<TreeNode> navTree1, final GroupTreeItem navTreeRoot1) {
navTree1.setRoot(navTreeRoot1);
navTreeRoot1.setExpanded(true);
navTree1.setCellFactory(treeView -> new GroupTreeCell());
navTree1.setShowRoot(false);
navTree1.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
navTree1.getSelectionModel().selectedItemProperty().addListener(o -> updateControllersGroup());
} }
private void rebuildTrees() { private void rebuildTrees() {
@ -207,38 +193,28 @@ final public class NavPanel extends TabPane {
*/ */
@ThreadConfined(type = ThreadType.JFX) @ThreadConfined(type = ThreadType.JFX)
private void setFocusedGroup(DrawableGroup grouping) { private void setFocusedGroup(DrawableGroup grouping) {
final GroupTreeItem treeItemForGroup = ((GroupTreeItem) activeTreeProperty.get().getRoot()).getTreeItemForPath(groupingToPath(grouping)); final GroupTreeItem treeItemForGroup = ((GroupTreeItem) activeTreeProperty.get().getRoot()).getTreeItemForPath(groupingToPath(grouping));
if (treeItemForGroup != null) { if (treeItemForGroup != null) {
/*
* When we used to run the below code on the FX thread, it would get
* into infinite loops when the next group button was pressed
* quickly because the udpates became out of order and History could
* not keep track of what was current.
*
* Currently (4/2/15), this method is already on the FX thread, so
* it is OK.
*/
//Platform.runLater(() -> {
activeTreeProperty.get().getSelectionModel().select(treeItemForGroup); activeTreeProperty.get().getSelectionModel().select(treeItemForGroup);
int row = activeTreeProperty.get().getRow(treeItemForGroup); Platform.runLater(() -> {
if (row != -1) { int row = activeTreeProperty.get().getRow(treeItemForGroup);
activeTreeProperty.get().scrollTo(row - 2); //put newly selected row 3 from the top if (row != -1) {
} activeTreeProperty.get().scrollTo(row - 2); //put newly selected row 3 from the top
//}); //end Platform.runLater }
});
} }
} }
private static List<String> groupingToPath(DrawableGroup g) { private List<String> groupingToPath(DrawableGroup g) {
if (g.groupKey.getAttribute() == DrawableAttribute.PATH) { if (g.groupKey.getAttribute() != DrawableAttribute.PATH
String path = g.groupKey.getValueDisplayName(); || activeTreeProperty.get() == hashTree) {
String stripStart = StringUtils.strip(g.groupKey.getValueDisplayName(), "/");
String[] cleanPathTokens = StringUtils.stripStart(path, "/").split("/"); return Arrays.asList(stripStart);
return Arrays.asList(cleanPathTokens);
} else { } else {
return Arrays.asList(g.groupKey.getValueDisplayName()); String path = g.groupKey.getValueDisplayName();
String[] cleanPathTokens = StringUtils.stripStart(path, "/").split("/");
return Arrays.asList(cleanPathTokens);
} }
} }

View File

@ -259,6 +259,15 @@ class TikaTextExtractor implements TextExtractor {
* This method determines if a passed-in Java char (16 bits) is a valid * This method determines if a passed-in Java char (16 bits) is a valid
* UTF-8 printable character, returning true if so, false if not. * UTF-8 printable character, returning true if so, false if not.
* *
* Note that this method can have ramifications for characters outside the
* Unicode Base Multilingual Plane (BMP), which require more than 16 bits.
* We are using Java characters (16 bits) to look at the data and this will
* not accurately identify any non-BMP character (larger than 16 bits)
* ending with 0xFFFF and 0xFFFE. In the interest of a fast solution, we
* have chosen to ignore the extended planes above Unicode BMP for the time
* being. The net result of this is some non-BMP characters may be
* interspersed with '^' characters in Autopsy.
*
* @param ch the character to test * @param ch the character to test
* *
* @return Returns true if the character is valid UTF-8, false if not. * @return Returns true if the character is valid UTF-8, false if not.