From b37c53f3a9cfe035e23d4a4ff64144e2d71b6708 Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 14 Jan 2016 15:00:40 -0500 Subject: [PATCH 1/4] improve cancellation and logging in ImageUtils and ThumbnailCache --- .../autopsy/coreutils/ImageUtils.java | 43 ++++++++++++++----- .../autopsy/imagegallery/ThumbnailCache.java | 3 +- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java index 5cbb3239cf..db5d1bc9d9 100755 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java @@ -234,22 +234,29 @@ public class ImageUtils { */ public static boolean isGIF(AbstractFile file) { try { - final FileTypeDetector fileTypeDetector = getFileTypeDetector(); - if (nonNull(fileTypeDetector)) { - String fileType = fileTypeDetector.getFileType(file); + final FileTypeDetector myFileTypeDetector = getFileTypeDetector(); + if (nonNull(myFileTypeDetector)) { + String fileType = myFileTypeDetector.getFileType(file); return IMAGE_GIF_MIME.equalsIgnoreCase(fileType); } - } catch (TskCoreException | FileTypeDetectorInitException ex) { - LOGGER.log(Level.WARNING, "Failed to get mime type with FileTypeDetector.", ex); //NOI18N + } catch (FileTypeDetectorInitException ex) { + 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)) { case TRUE: return true; case UNDEFINED: LOGGER.log(Level.WARNING, "Falling back on extension check."); //NOI18N - return "gif".equals(file.getNameExtension()); //NOI18N + return "gif".equalsIgnoreCase(file.getNameExtension()); //NOI18N case FALSE: default: return false; @@ -660,6 +667,9 @@ public class ImageUtils { if (isGIF(file)) { return readImage(); } + if (isCancelled()) { + return null; + } // If a thumbnail file is already saved locally, just read that. 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. BufferedImage thumbnail = null; @@ -722,6 +735,11 @@ public class ImageUtils { throw e; } } + + if (isCancelled()) { + return null; + } + updateProgress(-1, 1); //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 } - + if (isCancelled()) { + return null; + } try (ImageInputStream input = ImageIO.createImageInputStream(inputStream)) { if (input == null) { throw new IIOException(COULD_NOT_CREATE_IMAGE_INPUT_STREAM); @@ -834,9 +854,6 @@ public class ImageUtils { param.setDestination(bufferedImage); try { bufferedImage = reader.read(0, param); //should always be same bufferedImage object - if (isCancelled()) { - return null; - } } catch (IOException iOException) { // 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 @@ -844,6 +861,9 @@ public class ImageUtils { reader.removeIIOReadProgressListener(this); reader.dispose(); } + if (isCancelled()) { + return null; + } return SwingFXUtils.toFXImage(bufferedImage, null); } else { 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 updateProgress(percentageDone, 100); if (isCancelled()) { + reader.removeIIOReadProgressListener(this); reader.abort(); reader.dispose(); } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java index 4392f59e2d..d01d8d7901 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java @@ -39,7 +39,6 @@ import javafx.scene.image.Image; import javafx.scene.image.WritableImage; import javax.annotation.Nullable; import javax.imageio.ImageIO; -import org.openide.util.Exceptions; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; @@ -198,7 +197,7 @@ public enum ThumbnailCache { try { cache.put(Long.MIN_VALUE, newGetThumbnailTask.get()); } 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); } } }); From 31aceaf26c6d46b9bd43bab01a602d8ec8c6c6e4 Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 14 Jan 2016 15:03:40 -0500 Subject: [PATCH 2/4] use weak listeners to prevent memory leak --- .../gui/drawableviews/DrawableTile.java | 19 ++++++++++--------- .../gui/drawableviews/DrawableTileBase.java | 10 ++++++---- .../gui/drawableviews/DrawableUIBase.java | 1 + 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTile.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTile.java index ba7c275063..674aad0260 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTile.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTile.java @@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.imagegallery.gui.drawableviews; import java.util.Objects; import java.util.logging.Level; import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.WeakChangeListener; import javafx.concurrent.Task; import javafx.fxml.FXML; 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 Logger LOGGER = Logger.getLogger(DrawableTile.class.getName()); + private final ChangeListener 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 @Override @@ -63,13 +72,7 @@ public class DrawableTile extends DrawableTileBase { imageView.fitHeightProperty().bind(Toolbar.getDefault(getController()).sizeSliderValue()); imageView.fitWidthProperty().bind(Toolbar.getDefault(getController()).sizeSliderValue()); - selectionModel.lastSelectedProperty().addListener((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"); - } - }); + selectionModel.lastSelectedProperty().addListener(new WeakChangeListener<>(lastSelectionListener)); } public DrawableTile(GroupPane gp, ImageGalleryController controller) { @@ -100,6 +103,4 @@ public class DrawableTile extends DrawableTileBase { return getFile().map(AbstractContent::getName).orElse(""); } - - } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java index 6a7bcd9f17..2058737b3c 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java @@ -25,7 +25,9 @@ import java.util.Collection; import java.util.Optional; import java.util.logging.Level; import javafx.application.Platform; +import javafx.beans.InvalidationListener; import javafx.beans.Observable; +import javafx.beans.WeakInvalidationListener; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; @@ -111,7 +113,6 @@ public abstract class DrawableTileBase extends DrawableUIBase { @FXML private ImageView hashHitImageView; - /** * displays the icon representing follow up tag */ @@ -148,7 +149,7 @@ public abstract class DrawableTileBase extends DrawableUIBase { super(controller); this.groupPane = groupPane; selectionModel = controller.getSelectionModel(); - selectionModel.getSelected().addListener((Observable observable) -> updateSelectionState()); + selectionModel.getSelected().addListener(new WeakInvalidationListener(selectionListener)); //set up mouse listener //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() { return groupPane; @@ -314,7 +316,7 @@ public abstract class DrawableTileBase extends DrawableUIBase { getFile().ifPresent(file -> { final boolean isVideo = file.isVideo(); final boolean hasHashSetHits = hasHashHit(); - + final String text = getTextForLabel(); Platform.runLater(() -> { @@ -322,7 +324,7 @@ public abstract class DrawableTileBase extends DrawableUIBase { fileTypeImageView.setVisible(isVideo); hashHitImageView.setManaged(hasHashSetHits); hashHitImageView.setVisible(hasHashSetHits); - + nameLabel.setText(text); nameLabel.setTooltip(new Tooltip(text)); }); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableUIBase.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableUIBase.java index aa143d805f..9dfb1df802 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableUIBase.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableUIBase.java @@ -157,6 +157,7 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView } }); myTask.setOnCancelled(cancelled -> { + imageTask = null; disposeContent(); }); From e428f2c04db03cd027a3edb6e10a623ae5131017 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 15 Jan 2016 14:59:24 -0500 Subject: [PATCH 3/4] adjust threading to fix NavPanel infinite circular updating; clean up NavPanel.java add Executor interface to ImageGalleryController cleanup task creation in ThumbnailCache.java --- .../imagegallery/ImageGalleryController.java | 11 ++- .../autopsy/imagegallery/ThumbnailCache.java | 8 +- .../imagegallery/actions/NextUnseenGroup.java | 35 +++++--- .../gui/drawableviews/DrawableUIBase.java | 7 +- .../imagegallery/gui/navpanel/NavPanel.java | 86 +++++++------------ 5 files changed, 71 insertions(+), 76 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index 541cc2950e..7f8ac94e13 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -24,6 +24,8 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.logging.Level; 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 * 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()); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java index d01d8d7901..67c4a10dfa 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ThumbnailCache.java @@ -43,6 +43,7 @@ import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; import org.sleuthkit.autopsy.imagegallery.gui.Toolbar; +import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils; import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.TskCoreException; @@ -183,12 +184,7 @@ public enum ThumbnailCache { public Task getThumbnailTask(DrawableFile file) { final Image thumbnail = cache.getIfPresent(file.getId()); if (thumbnail != null) { - return new Task() { - @Override - protected Image call() throws Exception { - return thumbnail; - } - }; + return TaskUtils.taskFrom(() -> thumbnail); } final Task newGetThumbnailTask = ImageUtils.newGetThumbnailTask(file.getAbstractFile(), MAX_THUMBNAIL_SIZE, false); newGetThumbnailTask.stateProperty().addListener((Observable observable) -> { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java index cb6814275c..0d446cda84 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java @@ -19,9 +19,9 @@ package org.sleuthkit.autopsy.imagegallery.actions; import java.util.Optional; -import javafx.application.Platform; import javafx.beans.Observable; import javafx.beans.binding.ObjectExpression; +import javafx.concurrent.Task; import javafx.event.ActionEvent; import javafx.scene.image.Image; import javafx.scene.image.ImageView; @@ -36,10 +36,10 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState; */ public class NextUnseenGroup extends Action { - private static final Image END - = new Image(NextUnseenGroup.class.getResourceAsStream("/org/sleuthkit/autopsy/imagegallery/images/control-stop.png")); - private static final Image ADVANCE - = new Image(NextUnseenGroup.class.getResourceAsStream("/org/sleuthkit/autopsy/imagegallery/images/control-double.png")); + private static final Image END = + new Image(NextUnseenGroup.class.getResourceAsStream("/org/sleuthkit/autopsy/imagegallery/images/control-stop.png")); + private static final Image ADVANCE = + 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 NEXT_UNSEEN_GROUP = "Next Unseen group"; @@ -53,25 +53,36 @@ public class NextUnseenGroup extends Action { //TODO: do we need both these listeners? controller.getGroupManager().getAnalyzedGroups().addListener((Observable observable) -> { - Platform.runLater(this::updateButton); + updateButton(); }); controller.getGroupManager().getUnSeenGroups().addListener((Observable observable) -> { - Platform.runLater(this::updateButton); - + updateButton(); }); setEventHandler((ActionEvent t) -> { + //fx-thread //if there is a group assigned to the view, mark it as seen Optional.ofNullable(controller.viewState()) .map(ObjectExpression::getValue) .map(GroupViewState::getGroup) .ifPresent(group -> controller.getGroupManager().markGroupSeen(group, true)); + controller.execute(new Task() { - if (false == controller.getGroupManager().getUnSeenGroups().isEmpty()) { - controller.advance(GroupViewState.tile(controller.getGroupManager().getUnSeenGroups().get(0)), true); - } - updateButton(); + @Override + protected Void call() throws Exception { + if (false == controller.getGroupManager().getUnSeenGroups().isEmpty()) { + controller.advance(GroupViewState.tile(controller.getGroupManager().getUnSeenGroups().get(0)), true); + } + return null; + } + + @Override + protected void succeeded() { + super.succeeded(); + updateButton(); + } + }); }); updateButton(); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableUIBase.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableUIBase.java index 9dfb1df802..d77eee7f44 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableUIBase.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableUIBase.java @@ -157,8 +157,11 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView } }); myTask.setOnCancelled(cancelled -> { - imageTask = null; - disposeContent(); + synchronized (DrawableUIBase.this) { + imageTask = null; + } + imageView.setImage(null); + imageBorder.setCenter(null); }); exec.execute(myTask); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/NavPanel.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/NavPanel.java index 5bf6f63f48..84c28832e2 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/NavPanel.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/NavPanel.java @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.imagegallery.gui.navpanel; import java.util.Arrays; import java.util.List; import javafx.application.Platform; -import javafx.beans.Observable; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; @@ -108,32 +107,12 @@ final public class NavPanel extends TabPane { sortByBox.setButtonCell(new TreeNodeComparators.ComparatorListCell()); sortByBox.setItems(FXCollections.observableArrayList(FXCollections.observableArrayList(TreeNodeComparators.values()))); sortByBox.getSelectionModel().select(TreeNodeComparators.HIT_COUNT); - sortByBox.getSelectionModel().selectedItemProperty().addListener((Observable o) -> { - //user action ->jfx thread - resortHashTree(); - }); + sortByBox.getSelectionModel().selectedItemProperty().addListener(o -> resortHashTree()); - navTree.setRoot(navTreeRoot); - navTreeRoot.setExpanded(true); - navTree.setCellFactory((TreeView p) -> new GroupTreeCell()); - navTree.setShowRoot(false); - navTree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); - - hashTree.setRoot(hashTreeRoot); - hashTreeRoot.setExpanded(true); - hashTree.setCellFactory((TreeView 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); + configureTree(navTree, navTreeRoot); + configureTree(hashTree, hashTreeRoot); + activeTreeProperty.set(navTree); navTabPane.getSelectionModel().selectedItemProperty().addListener((ObservableValue ov, Tab t, Tab t1) -> { if (t1 == hashTab) { activeTreeProperty.set(hashTree); @@ -143,7 +122,6 @@ final public class NavPanel extends TabPane { }); controller.getGroupManager().getAnalyzedGroups().addListener((ListChangeListener.Change change) -> { - while (change.next()) { for (DrawableGroup g : change.getAddedSubList()) { insertIntoNavTree(g); @@ -158,15 +136,23 @@ final public class NavPanel extends TabPane { } }); - rebuildTrees(); - controller.viewState().addListener((ObservableValue observable, GroupViewState oldValue, GroupViewState newValue) -> { if (newValue != null && newValue.getGroup() != null) { - Platform.runLater(() -> { - setFocusedGroup(newValue.getGroup()); - }); + setFocusedGroup(newValue.getGroup()); } }); + + rebuildTrees(); + } + + @ThreadConfined(type = ThreadType.JFX) + private void configureTree(final TreeView 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() { @@ -207,38 +193,28 @@ final public class NavPanel extends TabPane { */ @ThreadConfined(type = ThreadType.JFX) private void setFocusedGroup(DrawableGroup grouping) { - final GroupTreeItem treeItemForGroup = ((GroupTreeItem) activeTreeProperty.get().getRoot()).getTreeItemForPath(groupingToPath(grouping)); 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); - int row = activeTreeProperty.get().getRow(treeItemForGroup); - if (row != -1) { - activeTreeProperty.get().scrollTo(row - 2); //put newly selected row 3 from the top - } - //}); //end Platform.runLater + Platform.runLater(() -> { + int row = activeTreeProperty.get().getRow(treeItemForGroup); + if (row != -1) { + activeTreeProperty.get().scrollTo(row - 2); //put newly selected row 3 from the top + } + }); } } - private static List groupingToPath(DrawableGroup g) { - if (g.groupKey.getAttribute() == DrawableAttribute.PATH) { - String path = g.groupKey.getValueDisplayName(); - - String[] cleanPathTokens = StringUtils.stripStart(path, "/").split("/"); - - return Arrays.asList(cleanPathTokens); + private List groupingToPath(DrawableGroup g) { + if (g.groupKey.getAttribute() != DrawableAttribute.PATH + || activeTreeProperty.get() == hashTree) { + String stripStart = StringUtils.strip(g.groupKey.getValueDisplayName(), "/"); + return Arrays.asList(stripStart); } else { - return Arrays.asList(g.groupKey.getValueDisplayName()); + String path = g.groupKey.getValueDisplayName(); + String[] cleanPathTokens = StringUtils.stripStart(path, "/").split("/"); + return Arrays.asList(cleanPathTokens); } } From f84002a389fa2a63ee29f4c9f6ad3fff15d142af Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 15 Jan 2016 15:58:37 -0500 Subject: [PATCH 4/4] fix bug resulting in invalid sql --- .../imagegallery/ImageGalleryController.java | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index 7f8ac94e13..9d374b3478 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -847,7 +847,7 @@ public final class ImageGalleryController implements Executor { /** * Copy files from a newly added data source into the DB. Get all * "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 */ @Override @@ -856,30 +856,28 @@ public final class ImageGalleryController implements Executor { updateMessage("prepopulating image/video database"); 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) { List fileSystems = ((Image) dataSource).getFileSystems(); - if (fileSystems.isEmpty() == false) { + if (fileSystems.isEmpty()) { /* * 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(); 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())) - .collect(Collectors.joining(" OR 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) "; + .collect(Collectors.joining(" OR fs_obj_id = ", "(fs_obj_id = ", ") ")); } final List files = getSleuthKitCase().findAllFilesWhere(fsQuery + " AND " + DRAWABLE_QUERY);