From b495209baa1311f5771cfb1c988a7cbefc091844 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Tue, 19 Jun 2018 18:22:03 -0700 Subject: [PATCH 01/21] added Callable, PropertyChangeListener --- .../autopsy/commonfilesearch/Md5Metadata.java | 16 ++++++ .../autopsy/datamodel/InstanceCountNode.java | 40 ++++++++++++-- .../sleuthkit/autopsy/datamodel/Md5Node.java | 52 +++++++++++++++++-- 3 files changed, 100 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java index 39c2cf8040..74891151fe 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java @@ -19,6 +19,9 @@ */ package org.sleuthkit.autopsy.commonfilesearch; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -32,10 +35,12 @@ final public class Md5Metadata { private final String md5; private final List fileInstances; + private final PropertyChangeSupport pss; Md5Metadata(String md5, List fileInstances){ this.md5 = md5; this.fileInstances = fileInstances; + this.pss = new PropertyChangeSupport(this); } public String getMd5(){ @@ -65,4 +70,15 @@ final public class Md5Metadata { } return String.join(", ", sources); } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + this.pss.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + this.pss.removePropertyChangeListener(listener); + } + + + } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java index cc7f23eda7..1bf2ef896f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java @@ -19,17 +19,21 @@ */ package org.sleuthkit.autopsy.datamodel; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.commonfilesearch.Md5Metadata; /** @@ -41,13 +45,35 @@ final public class InstanceCountNode extends DisplayableItemNode { final private List metadataList; public InstanceCountNode(int instanceCount, List md5Metadata) { - super(Children.create(new Md5NodeFactory(md5Metadata), true)); + super(Children.createLazy(new InstanceCountChildCallable(md5Metadata)), Lookups.singleton(instanceCount)); this.instanceCount = instanceCount; this.metadataList = md5Metadata; this.setDisplayName(Integer.toString(instanceCount)); } + + /** + * Callable wrapper to further delay lazy ChildFactory creation + * and createNodes() call once lazy loading is functional. + */ + private static class InstanceCountChildCallable implements Callable { + private final List key; + private InstanceCountChildCallable(List key) { + this.key = key; + } + @Override + public Children call() throws Exception { + return Children.create(new Md5NodeFactory(key), true); + + } + } + + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("ADD")) { + setChildren(Children.create(new Md5NodeFactory(metadataList), false)); + } + } int getInstanceCount() { return this.instanceCount; @@ -127,7 +153,7 @@ final public class InstanceCountNode extends DisplayableItemNode { * ChildFactory which builds CommonFileParentNodes from the * CommonFilesMetaaData models. */ - static class Md5NodeFactory extends ChildFactory { + static class Md5NodeFactory extends ChildFactory implements PropertyChangeListener { /** * List of models, each of which is a parent node matching a single md5, @@ -137,7 +163,7 @@ final public class InstanceCountNode extends DisplayableItemNode { Md5NodeFactory(List metadata) { this.metadata = new HashMap<>(); - + Iterator iterator = metadata.iterator(); while (iterator.hasNext()) { Md5Metadata md5Metadata = iterator.next(); @@ -156,5 +182,13 @@ final public class InstanceCountNode extends DisplayableItemNode { list.addAll(this.metadata.keySet()); return true; } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("ADD") || evt.getPropertyName().equals("REMOVE")) { + this.refresh(true); + } + } + } } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java b/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java index 4ef47605b4..fc06be3d77 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java @@ -19,15 +19,19 @@ */ package org.sleuthkit.autopsy.datamodel; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; import java.util.logging.Level; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.commonfilesearch.FileInstanceMetadata; @@ -43,24 +47,53 @@ import org.sleuthkit.datamodel.TskCoreException; * the MD5 of the matched files, the data sources those files were found within, * and a count of the instances represented by the md5. */ -public class Md5Node extends DisplayableItemNode { +public class Md5Node extends DisplayableItemNode implements PropertyChangeListener { private static final Logger LOGGER = Logger.getLogger(Md5Node.class.getName()); private final String md5Hash; private final int commonFileCount; private final String dataSources; + private final Md5Metadata metaData; public Md5Node(Md5Metadata data) { - super(Children.create( - new FileInstanceNodeFactory(data), true)); + super(Children.createLazy(new Md5ChildCallable(data)), Lookups.singleton(data.getMd5())); + this.commonFileCount = data.size(); this.dataSources = String.join(", ", data.getDataSources()); this.md5Hash = data.getMd5(); - + this.metaData = data; + metaData.addPropertyChangeListener(this); this.setDisplayName(this.md5Hash); } + + /** + * Callable wrapper to further delay lazy ChildFactory creation + * and createNodes() call once lazy loading is functional. + */ + private static class Md5ChildCallable implements Callable { + private final Md5Metadata key; + private Md5ChildCallable(Md5Metadata key) { + this.key = key; + } + @Override + public Children call() throws Exception { + //Check that the key has children, + //if it doesn't have children, return a leaf: + if (key.getMetadata().isEmpty()) { + return Children.LEAF; + } else { + return Children.create(new FileInstanceNodeFactory(key), true); + } + } + } + + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("ADD")) { + setChildren(Children.create(new FileInstanceNodeFactory(metaData), false)); + } + } int getCommonFileCount() { return this.commonFileCount; @@ -125,12 +158,13 @@ public class Md5Node extends DisplayableItemNode { /** * Child generator for FileInstanceNode of Md5Node. */ - static class FileInstanceNodeFactory extends ChildFactory { + static class FileInstanceNodeFactory extends ChildFactory implements PropertyChangeListener { private final Md5Metadata descendants; FileInstanceNodeFactory(Md5Metadata descendants) { this.descendants = descendants; + descendants.addPropertyChangeListener(this); } @Override @@ -152,6 +186,13 @@ public class Md5Node extends DisplayableItemNode { list.addAll(this.descendants.getMetadata()); return true; } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("ADD") || evt.getPropertyName().equals("REMOVE")) { + this.refresh(true); + } + } } @NbBundle.Messages({ @@ -175,4 +216,5 @@ public class Md5Node extends DisplayableItemNode { return this.displayString; } } + } From 06cbc75032949ff0f0fe3660c1de3c276c63ce7c Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Wed, 20 Jun 2018 10:56:40 -0700 Subject: [PATCH 02/21] delayed Md5Node creation. --- .../commonfilesearch/CommonFilesMetadata.java | 26 ++++--- .../CommonFilesMetadataBuilder.java | 10 +-- .../commonfilesearch/CommonFilesNode.java | 28 ++++++-- .../commonfilesearch/CommonFilesPanel.java | 2 +- .../autopsy/commonfilesearch/Md5Metadata.java | 2 +- .../commonfilesearch/Md5MetadataList.java | 62 ++++++++++++++++ .../corecomponents/DataResultViewerTable.java | 70 +++++++++++++++++++ .../MultiLayerTableFilterNode.java | 6 ++ .../autopsy/datamodel/InstanceCountNode.java | 70 +++++++++++++------ .../directorytree/DataResultFilterNode.java | 7 ++ 10 files changed, 241 insertions(+), 42 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5MetadataList.java diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java index b04792fb6b..c755b7a12a 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java @@ -20,7 +20,7 @@ package org.sleuthkit.autopsy.commonfilesearch; import java.util.Collections; -import java.util.List; +import java.util.HashMap; import java.util.Map; /** @@ -29,7 +29,8 @@ import java.util.Map; */ final public class CommonFilesMetadata { - private final Map> metadata; + private final Map metadata; + private final Map hiddenMetaData; /** * Create a metadata object which can be handed off to the node @@ -37,8 +38,13 @@ final public class CommonFilesMetadata { * * @param metadata list of Md5Metadata indexed by size of Md5Metadata */ - CommonFilesMetadata(Map> metadata){ - this.metadata = metadata; + CommonFilesMetadata(Map metadata){ + Map tempMetadata = new HashMap<>(); + for(Integer key : metadata.keySet()) { + tempMetadata.put(key, new Md5MetadataList()); + } + this.metadata = tempMetadata; + this.hiddenMetaData = metadata; } /** @@ -50,11 +56,15 @@ final public class CommonFilesMetadata { * @param md5 key * @return */ - List getMetadataForMd5(Integer instanceCount) { + Md5MetadataList getMetadataForMd5(Integer instanceCount) { return this.metadata.get(instanceCount); } + + public void displayHiddenMetadata() { + this.metadata.putAll(this.hiddenMetaData); + } - public Map> getMetadata() { + public Map getMetadata() { return Collections.unmodifiableMap(this.metadata); } @@ -65,8 +75,8 @@ final public class CommonFilesMetadata { public int size() { int count = 0; - for (List data : this.metadata.values()) { - for(Md5Metadata md5 : data){ + for (Md5MetadataList data : this.metadata.values()) { + for(Md5Metadata md5 : data.getMetadataList()){ count += md5.size(); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java index 9c04abd9c5..3865c7a7e6 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java @@ -200,17 +200,17 @@ public abstract class CommonFilesMetadataBuilder { } } - Map> instanceCollatedCommonFiles = new TreeMap<>(); + Map instanceCollatedCommonFiles = new TreeMap<>(); for(Md5Metadata md5Metadata : commonFiles.values()){ Integer size = md5Metadata.size(); if(instanceCollatedCommonFiles.containsKey(size)){ - instanceCollatedCommonFiles.get(size).add(md5Metadata); + instanceCollatedCommonFiles.get(size).addMetadataToList(md5Metadata); } else { - ArrayList value = new ArrayList(); - value.add(md5Metadata); - instanceCollatedCommonFiles.put(size, value); + Md5MetadataList metaList = new Md5MetadataList(); + metaList.addMetadataToList(md5Metadata); + instanceCollatedCommonFiles.put(size, metaList); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java index aa6d310219..2c72b0c804 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.commonfilesearch; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import org.sleuthkit.autopsy.datamodel.InstanceCountNode; import java.util.List; import org.openide.nodes.ChildFactory; @@ -33,9 +35,12 @@ import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; */ final public class CommonFilesNode extends DisplayableItemNode { - + private final CommonFilesMetadata metadataList; + CommonFilesNode(CommonFilesMetadata metadataList) { super(Children.create(new InstanceCountNodeFactory(metadataList), true)); + this.metadataList = metadataList; + this.refresh(); } @NbBundle.Messages({ @@ -59,8 +64,16 @@ final public class CommonFilesNode extends DisplayableItemNode { public String getItemType() { return getClass().getName(); } + + public void setCleanRefreshNeeded(boolean b) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + public void refresh() { + metadataList.displayHiddenMetadata(); + } - static class InstanceCountNodeFactory extends ChildFactory{ + static class InstanceCountNodeFactory extends ChildFactory implements PropertyChangeListener { private final CommonFilesMetadata metadata; @@ -76,8 +89,15 @@ final public class CommonFilesNode extends DisplayableItemNode { @Override protected Node createNodeForKey(Integer instanceCount){ - List md5Metadata = this.metadata.getMetadataForMd5(instanceCount); + Md5MetadataList md5Metadata = this.metadata.getMetadataForMd5(instanceCount); return new InstanceCountNode(instanceCount, md5Metadata); - } + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("ADD") || evt.getPropertyName().equals("REMOVE")) { + this.refresh(true); + } + } } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java index 5cf77ffd58..8b3004637a 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java @@ -247,7 +247,7 @@ public final class CommonFilesPanel extends javax.swing.JPanel { MultiLayerTableFilterNode tableFilterWithDescendantsNode = new MultiLayerTableFilterNode(dataResultFilterNode, 3); - DataResultViewerTable table = new DataResultViewerTable(); + DataResultViewerTable table = new DataResultViewerTable(true); Collection viewers = new ArrayList<>(1); viewers.add(table); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java index 74891151fe..9e19cce9ae 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java @@ -19,9 +19,9 @@ */ package org.sleuthkit.autopsy.commonfilesearch; -import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5MetadataList.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5MetadataList.java new file mode 100644 index 0000000000..d24df739eb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5MetadataList.java @@ -0,0 +1,62 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit 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.commonfilesearch; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Utility and wrapper model around data required for Common Files Search results. + * Subclass this to implement different selections of files from the case. + */ +final public class Md5MetadataList { + + private final List metadataList; + private final List delayedMetadataList; + + /** + * Create a metadata object containing the list of metadata which can be handed off to the node + * factories. + * + * @param metadata list of Md5Metadata indexed by size of Md5Metadata + */ + Md5MetadataList(List metadata){ + this.metadataList = new ArrayList(); + this.delayedMetadataList = metadata; + } + + Md5MetadataList(){ + this.metadataList = new ArrayList<>(); + this.delayedMetadataList = new ArrayList<>(); + } + + public List getMetadataList() { + return Collections.unmodifiableList(this.metadataList); + } + + public void displayDelayedMetadata() { + this.metadataList.addAll(this.delayedMetadataList); + } + + public void addMetadataToList(Md5Metadata metadata) { + delayedMetadataList.add(metadata); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 9e27700481..732347d85d 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -44,9 +44,11 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ListSelectionEvent; import javax.swing.event.TableColumnModelEvent; import javax.swing.event.TableColumnModelListener; +import javax.swing.event.TreeExpansionEvent; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; +import javax.swing.event.TreeExpansionListener; import org.netbeans.swing.etable.ETableColumn; import org.netbeans.swing.etable.ETableColumnModel; import org.netbeans.swing.outline.DefaultOutlineCellRenderer; @@ -54,6 +56,7 @@ import org.netbeans.swing.outline.DefaultOutlineModel; import org.netbeans.swing.outline.Outline; import org.openide.explorer.ExplorerManager; import org.openide.explorer.view.OutlineView; +import org.openide.explorer.view.Visualizer; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -61,10 +64,13 @@ import org.openide.nodes.Node.Property; import org.openide.util.NbBundle; import org.openide.util.NbPreferences; import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesNode; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; +import org.sleuthkit.autopsy.datamodel.InstanceCountNode; import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; +import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; /** * A tabular result viewer that displays the children of the given root node @@ -102,6 +108,70 @@ public final class DataResultViewerTable extends AbstractDataResultViewer { public DataResultViewerTable() { this(null, Bundle.DataResultViewerTable_title()); } + /** + * Constructs a tabular result viewer that displays the children of the + * given root node using an OutlineView. The viewer should have an ancestor + * top component to connect the lookups of the nodes displayed in the + * OutlineView to the actions global context. The explorer manager will be + * discovered at runtime. + */ + public DataResultViewerTable(boolean addListener) { + this(null, Bundle.DataResultViewerTable_title()); + outlineView.addTreeExpansionListener(new LazyLoadChildNodesOnTreeExpansion()); + } + + /** + * A tree expansion listener that will trigger a recreation of childs through its + * child factory on re-expansion of a node (causes to recreate the ChildFactory for + * this purpose.). + */ + private class LazyLoadChildNodesOnTreeExpansion implements TreeExpansionListener { + + /** + * A flag for avoiding endless recursion inside the expansion listener that could + * trigger collapsing and (re-)expanding nodes again. + */ + private boolean inRecursion = false; + + @Override + public synchronized void treeCollapsed(final TreeExpansionEvent event) { + Node eventNode = Visualizer.findNode(event.getPath().getLastPathComponent()); + if (!inRecursion && eventNode instanceof CommonFilesNode) { // avoid endless + // recursion + final CommonFilesNode node = (CommonFilesNode) eventNode; + node.setCleanRefreshNeeded(true); + } + } + + @Override + public synchronized void treeExpanded(final TreeExpansionEvent event) { + Node eventNode = Visualizer.findNode(event.getPath().getLastPathComponent()); + if (!inRecursion && eventNode instanceof MultiLayerTableFilterNode) { + final MultiLayerTableFilterNode node = (MultiLayerTableFilterNode) eventNode; + Node innerNode = node.getInnerNode(); + if(innerNode instanceof DataResultFilterNode) { + + final DataResultFilterNode drNode = (DataResultFilterNode) innerNode; + Node innerInnerNode = drNode.getInnerNode(); + if(innerInnerNode instanceof InstanceCountNode) { + final InstanceCountNode cfNode = (InstanceCountNode) innerInnerNode; + cfNode.refresh(); + }} + + if (!outlineView.isExpanded(node)) { + // Seems that the refresh caused to collapse, re-expand again and + // avoid recursion in this listener! + inRecursion = true; + try { + outlineView.expandNode(node); + } finally { + inRecursion = false; + } + } + } + + } + } /** * Constructs a tabular result viewer that displays the children of a given diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiLayerTableFilterNode.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiLayerTableFilterNode.java index c74f02f32d..b83d045c91 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiLayerTableFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiLayerTableFilterNode.java @@ -36,6 +36,7 @@ public class MultiLayerTableFilterNode extends FilterNode implements TableFilter private final boolean createChildren; private final boolean forceUseWrappedDisplayName; private static final String columnOrderKey = "NONE"; + private final Node innerNode; /** * Constructs a filter node that generates children using @@ -49,6 +50,11 @@ public class MultiLayerTableFilterNode extends FilterNode implements TableFilter super(node, TableFilterChildrenWithDescendants.createInstance(node, true, childLayerDepth), Lookups.proxy(node)); this.createChildren = true; this.forceUseWrappedDisplayName = true; + this.innerNode = node; + } + + public Node getInnerNode() { + return innerNode; } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java index 1bf2ef896f..cf86f7a613 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java @@ -20,8 +20,6 @@ package org.sleuthkit.autopsy.datamodel; import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; @@ -31,10 +29,15 @@ import java.util.concurrent.Callable; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; +import org.openide.nodes.NodeEvent; +import org.openide.nodes.NodeListener; +import org.openide.nodes.NodeMemberEvent; +import org.openide.nodes.NodeReorderEvent; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.commonfilesearch.Md5Metadata; +import org.sleuthkit.autopsy.commonfilesearch.Md5MetadataList; /** * @@ -42,9 +45,9 @@ import org.sleuthkit.autopsy.commonfilesearch.Md5Metadata; final public class InstanceCountNode extends DisplayableItemNode { final private int instanceCount; - final private List metadataList; + final private Md5MetadataList metadataList; - public InstanceCountNode(int instanceCount, List md5Metadata) { + public InstanceCountNode(int instanceCount, Md5MetadataList md5Metadata) { super(Children.createLazy(new InstanceCountChildCallable(md5Metadata)), Lookups.singleton(instanceCount)); this.instanceCount = instanceCount; @@ -58,20 +61,20 @@ final public class InstanceCountNode extends DisplayableItemNode { * and createNodes() call once lazy loading is functional. */ private static class InstanceCountChildCallable implements Callable { - private final List key; - private InstanceCountChildCallable(List key) { + private final Md5MetadataList key; + private InstanceCountChildCallable(Md5MetadataList key) { this.key = key; } @Override public Children call() throws Exception { - return Children.create(new Md5NodeFactory(key), true); + return Children.create(new Md5NodeFactory(key.getMetadataList()), true); } } public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals("ADD")) { - setChildren(Children.create(new Md5NodeFactory(metadataList), false)); + setChildren(Children.create(new Md5NodeFactory(metadataList.getMetadataList()), false)); } } @@ -80,7 +83,7 @@ final public class InstanceCountNode extends DisplayableItemNode { } List getMetadata() { - return Collections.unmodifiableList(this.metadataList); + return this.metadataList.getMetadataList(); } @Override @@ -92,6 +95,16 @@ final public class InstanceCountNode extends DisplayableItemNode { public boolean isLeafTypeNode() { return false; } + + public void setCleanRefreshNeeded(boolean b) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + public void refresh() { + metadataList.displayDelayedMetadata(); + //firePropertyChange(PROP_NAME, this, this); + setChildren(Children.create(new Md5NodeFactory(metadataList.getMetadataList()), false)); + } @Override public String getItemType() { @@ -153,33 +166,26 @@ final public class InstanceCountNode extends DisplayableItemNode { * ChildFactory which builds CommonFileParentNodes from the * CommonFilesMetaaData models. */ - static class Md5NodeFactory extends ChildFactory implements PropertyChangeListener { + static class Md5NodeFactory extends ChildFactory implements NodeListener { /** * List of models, each of which is a parent node matching a single md5, * containing children FileNodes. */ - private final Map metadata; + private final List metadata; Md5NodeFactory(List metadata) { - this.metadata = new HashMap<>(); - - Iterator iterator = metadata.iterator(); - while (iterator.hasNext()) { - Md5Metadata md5Metadata = iterator.next(); - this.metadata.put(md5Metadata.getMd5(), md5Metadata); - } + this.metadata = metadata; } @Override - protected Node createNodeForKey(String md5) { - Md5Metadata md5Metadata = this.metadata.get(md5); - return new Md5Node(md5Metadata); + protected Node createNodeForKey(Md5Metadata metadataKey) { + return new Md5Node(metadataKey); } @Override - protected boolean createKeys(List list) { - list.addAll(this.metadata.keySet()); + protected boolean createKeys(List list) { + list.addAll(metadata); return true; } @@ -189,6 +195,24 @@ final public class InstanceCountNode extends DisplayableItemNode { this.refresh(true); } } + + @Override + public void childrenAdded(NodeMemberEvent nme) { + } + @Override + public void childrenRemoved(NodeMemberEvent nme) { + } + + @Override + public void childrenReordered(NodeReorderEvent nre) { + + } + + @Override + public void nodeDestroyed(NodeEvent ne) { + + } + } } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index a23f045f48..a9e841abb3 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -98,6 +98,7 @@ public class DataResultFilterNode extends FilterNode { private static boolean filterKnownFromViews = UserPreferences.hideKnownFilesInViewsTree(); private static boolean filterSlackFromDataSources = UserPreferences.hideSlackFilesInDataSourcesTree(); private static boolean filterSlackFromViews = UserPreferences.hideSlackFilesInViewsTree(); + private Node innerNode; static { UserPreferences.addChangeListener(new PreferenceChangeListener() { @@ -138,6 +139,11 @@ public class DataResultFilterNode extends FilterNode { public DataResultFilterNode(Node node, ExplorerManager em) { super(node, new DataResultFilterChildren(node, em)); this.sourceEm = em; + this.innerNode = node; + } + + public Node getInnerNode() { + return innerNode; } /** @@ -156,6 +162,7 @@ public class DataResultFilterNode extends FilterNode { private DataResultFilterNode(Node node, ExplorerManager em, boolean filterKnown, boolean filterSlack) { super(node, new DataResultFilterChildren(node, em, filterKnown, filterSlack)); this.sourceEm = em; + this.innerNode = node; } /** From 9aae3b4a1cf91487d7a2bef8e08f0ac24313c501 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Wed, 20 Jun 2018 18:03:35 -0700 Subject: [PATCH 03/21] Cleanup, and polish to minimal changeset. Unused callables. --- .../commonfilesearch/CommonFilesMetadata.java | 12 +--- .../commonfilesearch/CommonFilesNode.java | 21 +----- .../commonfilesearch/CommonFilesPanel.java | 5 +- .../autopsy/commonfilesearch/Md5Metadata.java | 14 +--- .../commonfilesearch/Md5MetadataList.java | 29 ++++---- .../corecomponents/DataResultViewerTable.java | 58 ++------------- .../DelayedLoadChildNodesOnTreeExpansion.java | 70 +++++++++++++++++++ .../MultiLayerTableFilterNode.java | 9 ++- .../autopsy/datamodel/InstanceCountNode.java | 46 +----------- .../sleuthkit/autopsy/datamodel/Md5Node.java | 27 +------ .../directorytree/DataResultFilterNode.java | 49 +++++++------ 11 files changed, 133 insertions(+), 207 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java index c755b7a12a..90995ca00d 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java @@ -30,7 +30,6 @@ import java.util.Map; final public class CommonFilesMetadata { private final Map metadata; - private final Map hiddenMetaData; /** * Create a metadata object which can be handed off to the node @@ -39,12 +38,7 @@ final public class CommonFilesMetadata { * @param metadata list of Md5Metadata indexed by size of Md5Metadata */ CommonFilesMetadata(Map metadata){ - Map tempMetadata = new HashMap<>(); - for(Integer key : metadata.keySet()) { - tempMetadata.put(key, new Md5MetadataList()); - } - this.metadata = tempMetadata; - this.hiddenMetaData = metadata; + this.metadata = metadata; } /** @@ -59,10 +53,6 @@ final public class CommonFilesMetadata { Md5MetadataList getMetadataForMd5(Integer instanceCount) { return this.metadata.get(instanceCount); } - - public void displayHiddenMetadata() { - this.metadata.putAll(this.hiddenMetaData); - } public Map getMetadata() { return Collections.unmodifiableMap(this.metadata); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java index 2c72b0c804..07074d1885 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java @@ -35,12 +35,8 @@ import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; */ final public class CommonFilesNode extends DisplayableItemNode { - private final CommonFilesMetadata metadataList; - CommonFilesNode(CommonFilesMetadata metadataList) { super(Children.create(new InstanceCountNodeFactory(metadataList), true)); - this.metadataList = metadataList; - this.refresh(); } @NbBundle.Messages({ @@ -64,16 +60,8 @@ final public class CommonFilesNode extends DisplayableItemNode { public String getItemType() { return getClass().getName(); } - - public void setCleanRefreshNeeded(boolean b) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - public void refresh() { - metadataList.displayHiddenMetadata(); - } - static class InstanceCountNodeFactory extends ChildFactory implements PropertyChangeListener { + static class InstanceCountNodeFactory extends ChildFactory { private final CommonFilesMetadata metadata; @@ -92,12 +80,5 @@ final public class CommonFilesNode extends DisplayableItemNode { Md5MetadataList md5Metadata = this.metadata.getMetadataForMd5(instanceCount); return new InstanceCountNode(instanceCount, md5Metadata); } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals("ADD") || evt.getPropertyName().equals("REMOVE")) { - this.refresh(true); - } - } } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java index 8b3004637a..2bb9bbdd1e 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java @@ -34,6 +34,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; +import org.sleuthkit.autopsy.corecomponents.DelayedLoadChildNodesOnTreeExpansion; import org.sleuthkit.autopsy.corecomponents.MultiLayerTableFilterNode; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -242,12 +243,12 @@ public final class CommonFilesPanel extends javax.swing.JPanel { CommonFilesNode commonFilesNode = new CommonFilesNode(metadata); - //TODO this could be enumerating the children!!! + //TODO this could be enumerating the children!!! Instead delay enumeration by dynamically loading nodes with DelayedLoadChildNodesOnTreeExpansion() DataResultFilterNode dataResultFilterNode = new DataResultFilterNode(commonFilesNode, ExplorerManager.find(CommonFilesPanel.this)); MultiLayerTableFilterNode tableFilterWithDescendantsNode = new MultiLayerTableFilterNode(dataResultFilterNode, 3); - DataResultViewerTable table = new DataResultViewerTable(true); + DataResultViewerTable table = new DataResultViewerTable(new DelayedLoadChildNodesOnTreeExpansion()); Collection viewers = new ArrayList<>(1); viewers.add(table); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java index 9e19cce9ae..501a70e09b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java @@ -19,9 +19,6 @@ */ package org.sleuthkit.autopsy.commonfilesearch; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -35,12 +32,11 @@ final public class Md5Metadata { private final String md5; private final List fileInstances; - private final PropertyChangeSupport pss; Md5Metadata(String md5, List fileInstances){ this.md5 = md5; this.fileInstances = fileInstances; - this.pss = new PropertyChangeSupport(this); + } public String getMd5(){ @@ -70,14 +66,6 @@ final public class Md5Metadata { } return String.join(", ", sources); } - - public void addPropertyChangeListener(PropertyChangeListener listener) { - this.pss.addPropertyChangeListener(listener); - } - - public void removePropertyChangeListener(PropertyChangeListener listener) { - this.pss.removePropertyChangeListener(listener); - } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5MetadataList.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5MetadataList.java index d24df739eb..5d297b8cd0 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5MetadataList.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5MetadataList.java @@ -24,26 +24,27 @@ import java.util.Collections; import java.util.List; /** - * Utility and wrapper model around data required for Common Files Search results. - * Subclass this to implement different selections of files from the case. + * Utility and wrapper model around data required for Common Files Search + * results. Subclass this to implement different selections of files from the + * case. */ final public class Md5MetadataList { - + private final List metadataList; private final List delayedMetadataList; - + /** - * Create a metadata object containing the list of metadata which can be handed off to the node - * factories. - * + * Create a metadata object containing the list of metadata which can be + * handed off to the node factories. + * * @param metadata list of Md5Metadata indexed by size of Md5Metadata */ - Md5MetadataList(List metadata){ + Md5MetadataList(List metadata) { this.metadataList = new ArrayList(); this.delayedMetadataList = metadata; } - - Md5MetadataList(){ + + Md5MetadataList() { this.metadataList = new ArrayList<>(); this.delayedMetadataList = new ArrayList<>(); } @@ -51,11 +52,13 @@ final public class Md5MetadataList { public List getMetadataList() { return Collections.unmodifiableList(this.metadataList); } - + public void displayDelayedMetadata() { - this.metadataList.addAll(this.delayedMetadataList); + if(metadataList.isEmpty()) { + this.metadataList.addAll(this.delayedMetadataList); + } } - + public void addMetadataToList(Md5Metadata metadata) { delayedMetadataList.add(metadata); } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 732347d85d..9863a98174 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -114,64 +114,14 @@ public final class DataResultViewerTable extends AbstractDataResultViewer { * top component to connect the lookups of the nodes displayed in the * OutlineView to the actions global context. The explorer manager will be * discovered at runtime. + * @param listener */ - public DataResultViewerTable(boolean addListener) { + public DataResultViewerTable(TreeExpansionListener listener) { this(null, Bundle.DataResultViewerTable_title()); - outlineView.addTreeExpansionListener(new LazyLoadChildNodesOnTreeExpansion()); + outlineView.addTreeExpansionListener(listener); } - /** - * A tree expansion listener that will trigger a recreation of childs through its - * child factory on re-expansion of a node (causes to recreate the ChildFactory for - * this purpose.). - */ - private class LazyLoadChildNodesOnTreeExpansion implements TreeExpansionListener { - - /** - * A flag for avoiding endless recursion inside the expansion listener that could - * trigger collapsing and (re-)expanding nodes again. - */ - private boolean inRecursion = false; - - @Override - public synchronized void treeCollapsed(final TreeExpansionEvent event) { - Node eventNode = Visualizer.findNode(event.getPath().getLastPathComponent()); - if (!inRecursion && eventNode instanceof CommonFilesNode) { // avoid endless - // recursion - final CommonFilesNode node = (CommonFilesNode) eventNode; - node.setCleanRefreshNeeded(true); - } - } - - @Override - public synchronized void treeExpanded(final TreeExpansionEvent event) { - Node eventNode = Visualizer.findNode(event.getPath().getLastPathComponent()); - if (!inRecursion && eventNode instanceof MultiLayerTableFilterNode) { - final MultiLayerTableFilterNode node = (MultiLayerTableFilterNode) eventNode; - Node innerNode = node.getInnerNode(); - if(innerNode instanceof DataResultFilterNode) { - - final DataResultFilterNode drNode = (DataResultFilterNode) innerNode; - Node innerInnerNode = drNode.getInnerNode(); - if(innerInnerNode instanceof InstanceCountNode) { - final InstanceCountNode cfNode = (InstanceCountNode) innerInnerNode; - cfNode.refresh(); - }} - - if (!outlineView.isExpanded(node)) { - // Seems that the refresh caused to collapse, re-expand again and - // avoid recursion in this listener! - inRecursion = true; - try { - outlineView.expandNode(node); - } finally { - inRecursion = false; - } - } - } - - } - } + /** * Constructs a tabular result viewer that displays the children of a given diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java new file mode 100644 index 0000000000..bdc7e4b7c7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java @@ -0,0 +1,70 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit 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.corecomponents; + +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import org.openide.explorer.view.Visualizer; +import org.openide.nodes.Node; + +/** + * A tree expansion listener that will trigger a recreation of childs through + * its child factory on re-expansion of a node (causes to recreate the + * ChildFactory for this purpose.). + */ +public final class DelayedLoadChildNodesOnTreeExpansion implements TreeExpansionListener { + + /** + * A flag for avoiding endless recursion inside the expansion listener that + * could trigger collapsing and (re-)expanding nodes again. + * @param event + */ + //private boolean inRecursion = false; + + @Override + public synchronized void treeCollapsed(final TreeExpansionEvent event) { + Node eventNode = Visualizer.findNode(event.getPath().getLastPathComponent()); +// if (eventNode instanceof CommonFilesNode) { // avoid endless //!inRecursion && +// // recursion +// final CommonFilesNode node = (CommonFilesNode) eventNode; +// node.setCleanRefreshNeeded(true); +// } + } + + @Override + public synchronized void treeExpanded(final TreeExpansionEvent event) { + Node eventNode = Visualizer.findNode(event.getPath().getLastPathComponent()); + if (eventNode instanceof MultiLayerTableFilterNode) { //!inRecursion && + final MultiLayerTableFilterNode node = (MultiLayerTableFilterNode) eventNode; + node.refresh(); +// if (!outlineView.isExpanded(node)) { +// // Seems that the refresh caused to collapse, re-expand again and +// // avoid recursion in this listener! +// inRecursion = true; +// try { +// outlineView.expandNode(node); +// } finally { +// inRecursion = false; +// } +// } + } + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiLayerTableFilterNode.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiLayerTableFilterNode.java index b83d045c91..b5399ca2bc 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiLayerTableFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiLayerTableFilterNode.java @@ -52,9 +52,12 @@ public class MultiLayerTableFilterNode extends FilterNode implements TableFilter this.forceUseWrappedDisplayName = true; this.innerNode = node; } - - public Node getInnerNode() { - return innerNode; + + void refresh() { + if (innerNode instanceof DataResultFilterNode) { + final DataResultFilterNode drNode = (DataResultFilterNode) innerNode; + drNode.refresh(); + } } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java index cf86f7a613..7a092a91da 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java @@ -19,9 +19,6 @@ */ package org.sleuthkit.autopsy.datamodel; -import java.beans.PropertyChangeEvent; -import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -29,13 +26,8 @@ import java.util.concurrent.Callable; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; -import org.openide.nodes.NodeEvent; -import org.openide.nodes.NodeListener; -import org.openide.nodes.NodeMemberEvent; -import org.openide.nodes.NodeReorderEvent; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; -import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.commonfilesearch.Md5Metadata; import org.sleuthkit.autopsy.commonfilesearch.Md5MetadataList; @@ -48,7 +40,7 @@ final public class InstanceCountNode extends DisplayableItemNode { final private Md5MetadataList metadataList; public InstanceCountNode(int instanceCount, Md5MetadataList md5Metadata) { - super(Children.createLazy(new InstanceCountChildCallable(md5Metadata)), Lookups.singleton(instanceCount)); + super(Children.create(new Md5NodeFactory(md5Metadata.getMetadataList()), true)); //Children.createLazy(new InstanceCountChildCallable(md5Metadata)), Lookups.singleton(instanceCount)); this.instanceCount = instanceCount; this.metadataList = md5Metadata; @@ -71,12 +63,6 @@ final public class InstanceCountNode extends DisplayableItemNode { } } - - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals("ADD")) { - setChildren(Children.create(new Md5NodeFactory(metadataList.getMetadataList()), false)); - } - } int getInstanceCount() { return this.instanceCount; @@ -95,10 +81,6 @@ final public class InstanceCountNode extends DisplayableItemNode { public boolean isLeafTypeNode() { return false; } - - public void setCleanRefreshNeeded(boolean b) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } public void refresh() { metadataList.displayDelayedMetadata(); @@ -166,7 +148,7 @@ final public class InstanceCountNode extends DisplayableItemNode { * ChildFactory which builds CommonFileParentNodes from the * CommonFilesMetaaData models. */ - static class Md5NodeFactory extends ChildFactory implements NodeListener { + static class Md5NodeFactory extends ChildFactory { /** * List of models, each of which is a parent node matching a single md5, @@ -188,30 +170,6 @@ final public class InstanceCountNode extends DisplayableItemNode { list.addAll(metadata); return true; } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals("ADD") || evt.getPropertyName().equals("REMOVE")) { - this.refresh(true); - } - } - - @Override - public void childrenAdded(NodeMemberEvent nme) { - } - @Override - public void childrenRemoved(NodeMemberEvent nme) { - } - - @Override - public void childrenReordered(NodeReorderEvent nre) { - - } - - @Override - public void nodeDestroyed(NodeEvent ne) { - - } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java b/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java index fc06be3d77..47cd19e9af 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java @@ -19,8 +19,6 @@ */ package org.sleuthkit.autopsy.datamodel; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -31,7 +29,6 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; -import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.commonfilesearch.FileInstanceMetadata; @@ -47,24 +44,21 @@ import org.sleuthkit.datamodel.TskCoreException; * the MD5 of the matched files, the data sources those files were found within, * and a count of the instances represented by the md5. */ -public class Md5Node extends DisplayableItemNode implements PropertyChangeListener { +public class Md5Node extends DisplayableItemNode { private static final Logger LOGGER = Logger.getLogger(Md5Node.class.getName()); private final String md5Hash; private final int commonFileCount; private final String dataSources; - private final Md5Metadata metaData; public Md5Node(Md5Metadata data) { - super(Children.createLazy(new Md5ChildCallable(data)), Lookups.singleton(data.getMd5())); + super(Children.create(new FileInstanceNodeFactory(data), true)); //Children.createLazy(new Md5ChildCallable(data)), Lookups.singleton(data.getMd5())); this.commonFileCount = data.size(); this.dataSources = String.join(", ", data.getDataSources()); this.md5Hash = data.getMd5(); - this.metaData = data; - metaData.addPropertyChangeListener(this); this.setDisplayName(this.md5Hash); } @@ -88,13 +82,6 @@ public class Md5Node extends DisplayableItemNode implements PropertyChangeListen } } } - - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals("ADD")) { - setChildren(Children.create(new FileInstanceNodeFactory(metaData), false)); - } - } - int getCommonFileCount() { return this.commonFileCount; } @@ -158,13 +145,12 @@ public class Md5Node extends DisplayableItemNode implements PropertyChangeListen /** * Child generator for FileInstanceNode of Md5Node. */ - static class FileInstanceNodeFactory extends ChildFactory implements PropertyChangeListener { + static class FileInstanceNodeFactory extends ChildFactory { private final Md5Metadata descendants; FileInstanceNodeFactory(Md5Metadata descendants) { this.descendants = descendants; - descendants.addPropertyChangeListener(this); } @Override @@ -186,13 +172,6 @@ public class Md5Node extends DisplayableItemNode implements PropertyChangeListen list.addAll(this.descendants.getMetadata()); return true; } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals("ADD") || evt.getPropertyName().equals("REMOVE")) { - this.refresh(true); - } - } } @NbBundle.Messages({ diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index a9e841abb3..ed0cf41bae 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -133,17 +133,20 @@ public class DataResultFilterNode extends FilterNode { * wrapped node and may filter out some of its children. * * @param node The node to wrap. - * @param em The ExplorerManager for the component that is creating the - * node. + * @param em The ExplorerManager for the component that is creating the + * node. */ public DataResultFilterNode(Node node, ExplorerManager em) { super(node, new DataResultFilterChildren(node, em)); this.sourceEm = em; this.innerNode = node; } - - public Node getInnerNode() { - return innerNode; + + public void refresh() { + if (innerNode instanceof InstanceCountNode) { + final InstanceCountNode cfNode = (InstanceCountNode) innerNode; + cfNode.refresh(); + } } /** @@ -151,18 +154,18 @@ public class DataResultFilterNode extends FilterNode { * result viewers. The wrapper node defines the actions associated with the * wrapped node and may filter out some of its children. * - * @param node The node to wrap. - * @param em The ExplorerManager for the component that is creating - * the node. + * @param node The node to wrap. + * @param em The ExplorerManager for the component that is creating the + * node. * @param filterKnown Whether or not to filter out children that represent - * known files. + * known files. * @param filterSlack Whether or not to filter out children that represent - * virtual slack space files. + * virtual slack space files. */ private DataResultFilterNode(Node node, ExplorerManager em, boolean filterKnown, boolean filterSlack) { super(node, new DataResultFilterChildren(node, em, filterKnown, filterSlack)); this.sourceEm = em; - this.innerNode = node; + this.innerNode = node; } /** @@ -268,7 +271,7 @@ public class DataResultFilterNode extends FilterNode { * selected. * * @return The child node selection information, or null if no child should - * be selected. + * be selected. */ public NodeSelectionInfo getChildNodeSelectionInfo() { if (getOriginal() instanceof DisplayableItemNode) { @@ -397,14 +400,14 @@ public class DataResultFilterNode extends FilterNode { NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewFileInDir.text"), c)); } // action to go to the source file of the artifact - // action to go to the source file of the artifact + // action to go to the source file of the artifact Content fileContent = ban.getLookup().lookup(AbstractFile.class); if (fileContent == null) { Content content = ban.getLookup().lookup(Content.class); actionsList.add(new ViewContextAction("View Source Content in Directory", content)); } else { actionsList.add(new ViewContextAction( - NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewSrcFileInDir.text"), ban)); + NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewSrcFileInDir.text"), ban)); } } Content c = ban.getLookup().lookup(File.class); @@ -537,21 +540,21 @@ public class DataResultFilterNode extends FilterNode { */ private class GetPreferredActionsDisplayableItemNodeVisitor extends DisplayableItemNodeVisitor.Default { - @Override - public AbstractAction visit(InstanceCountNode icn){ - return new NoAction(); - } - @Override - public AbstractAction visit(Md5Node md5n){ + public AbstractAction visit(InstanceCountNode icn) { return new NoAction(); } - + @Override - public AbstractAction visit(FileInstanceNode fin){ + public AbstractAction visit(Md5Node md5n) { return new NoAction(); } - + + @Override + public AbstractAction visit(FileInstanceNode fin) { + return new NoAction(); + } + @Override public AbstractAction visit(BlackboardArtifactNode ban) { BlackboardArtifact artifact = ban.getArtifact(); From 629f00980ea44ffb6ae2d7319c62effb73231b26 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Wed, 20 Jun 2018 18:05:09 -0700 Subject: [PATCH 04/21] Remove callables. Format files. --- .../commonfilesearch/CommonFilesNode.java | 20 ++++----- .../commonfilesearch/Md5MetadataList.java | 4 +- .../autopsy/datamodel/InstanceCountNode.java | 38 +++++------------ .../sleuthkit/autopsy/datamodel/Md5Node.java | 41 +++++-------------- 4 files changed, 31 insertions(+), 72 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java index 07074d1885..67c8c096bc 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java @@ -18,8 +18,6 @@ */ package org.sleuthkit.autopsy.commonfilesearch; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import org.sleuthkit.autopsy.datamodel.InstanceCountNode; import java.util.List; import org.openide.nodes.ChildFactory; @@ -34,7 +32,7 @@ import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; * results in the top right pane. Calls Md5NodeFactory. */ final public class CommonFilesNode extends DisplayableItemNode { - + CommonFilesNode(CommonFilesMetadata metadataList) { super(Children.create(new InstanceCountNodeFactory(metadataList), true)); } @@ -60,25 +58,25 @@ final public class CommonFilesNode extends DisplayableItemNode { public String getItemType() { return getClass().getName(); } - + static class InstanceCountNodeFactory extends ChildFactory { private final CommonFilesMetadata metadata; - - InstanceCountNodeFactory(CommonFilesMetadata metadata){ + + InstanceCountNodeFactory(CommonFilesMetadata metadata) { this.metadata = metadata; } - + @Override protected boolean createKeys(List list) { list.addAll(this.metadata.getMetadata().keySet()); return true; } - + @Override - protected Node createNodeForKey(Integer instanceCount){ - Md5MetadataList md5Metadata = this.metadata.getMetadataForMd5(instanceCount); + protected Node createNodeForKey(Integer instanceCount) { + Md5MetadataList md5Metadata = this.metadata.getMetadataForMd5(instanceCount); return new InstanceCountNode(instanceCount, md5Metadata); - } + } } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5MetadataList.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5MetadataList.java index 5d297b8cd0..3e57ec7d55 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5MetadataList.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5MetadataList.java @@ -40,7 +40,7 @@ final public class Md5MetadataList { * @param metadata list of Md5Metadata indexed by size of Md5Metadata */ Md5MetadataList(List metadata) { - this.metadataList = new ArrayList(); + this.metadataList = new ArrayList<>(); this.delayedMetadataList = metadata; } @@ -54,7 +54,7 @@ final public class Md5MetadataList { } public void displayDelayedMetadata() { - if(metadataList.isEmpty()) { + if (metadataList.isEmpty()) { this.metadataList.addAll(this.delayedMetadataList); } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java index 7a092a91da..9283de6026 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java @@ -22,7 +22,6 @@ package org.sleuthkit.autopsy.datamodel; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.Callable; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -44,25 +43,9 @@ final public class InstanceCountNode extends DisplayableItemNode { this.instanceCount = instanceCount; this.metadataList = md5Metadata; - + this.setDisplayName(Integer.toString(instanceCount)); } - - /** - * Callable wrapper to further delay lazy ChildFactory creation - * and createNodes() call once lazy loading is functional. - */ - private static class InstanceCountChildCallable implements Callable { - private final Md5MetadataList key; - private InstanceCountChildCallable(Md5MetadataList key) { - this.key = key; - } - @Override - public Children call() throws Exception { - return Children.create(new Md5NodeFactory(key.getMetadataList()), true); - - } - } int getInstanceCount() { return this.instanceCount; @@ -124,22 +107,22 @@ final public class InstanceCountNode extends DisplayableItemNode { static private void fillPropertyMap(Map map, InstanceCountNode node) { map.put(InstanceCountNodePropertyType.Match.toString(), node.getInstanceCount()); } - + @NbBundle.Messages({ "InstanceCountNodeProeprtyType.matchCountColLbl1=Match" }) - public enum InstanceCountNodePropertyType{ - + public enum InstanceCountNodePropertyType { + Match(Bundle.InstanceCountNodeProeprtyType_matchCountColLbl1()); - + final private String displayString; - - private InstanceCountNodePropertyType(String displayName){ + + private InstanceCountNodePropertyType(String displayName) { this.displayString = displayName; } - + @Override - public String toString(){ + public String toString() { return this.displayString; } } @@ -171,6 +154,5 @@ final public class InstanceCountNode extends DisplayableItemNode { return true; } - } -} \ No newline at end of file +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java b/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java index 47cd19e9af..9ebc81a076 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java @@ -22,7 +22,6 @@ package org.sleuthkit.autopsy.datamodel; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.Callable; import java.util.logging.Level; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; @@ -40,14 +39,14 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Represents a common files match - two or more files which appear to be the - * same file and appear as children of this node. This node will simply contain + * same file and appear as children of this node. This node will simply contain * the MD5 of the matched files, the data sources those files were found within, * and a count of the instances represented by the md5. */ public class Md5Node extends DisplayableItemNode { - - private static final Logger LOGGER = Logger.getLogger(Md5Node.class.getName()); - + + private static final Logger LOGGER = Logger.getLogger(Md5Node.class.getName()); + private final String md5Hash; private final int commonFileCount; private final String dataSources; @@ -55,33 +54,12 @@ public class Md5Node extends DisplayableItemNode { public Md5Node(Md5Metadata data) { super(Children.create(new FileInstanceNodeFactory(data), true)); //Children.createLazy(new Md5ChildCallable(data)), Lookups.singleton(data.getMd5())); - this.commonFileCount = data.size(); this.dataSources = String.join(", ", data.getDataSources()); this.md5Hash = data.getMd5(); this.setDisplayName(this.md5Hash); } - - /** - * Callable wrapper to further delay lazy ChildFactory creation - * and createNodes() call once lazy loading is functional. - */ - private static class Md5ChildCallable implements Callable { - private final Md5Metadata key; - private Md5ChildCallable(Md5Metadata key) { - this.key = key; - } - @Override - public Children call() throws Exception { - //Check that the key has children, - //if it doesn't have children, return a leaf: - if (key.getMetadata().isEmpty()) { - return Children.LEAF; - } else { - return Children.create(new FileInstanceNodeFactory(key), true); - } - } - } + int getCommonFileCount() { return this.commonFileCount; } @@ -143,7 +121,8 @@ public class Md5Node extends DisplayableItemNode { } /** - * Child generator for FileInstanceNode of Md5Node. + * Child generator for FileInstanceNode of + * Md5Node. */ static class FileInstanceNodeFactory extends ChildFactory { @@ -159,7 +138,7 @@ public class Md5Node extends DisplayableItemNode { Case currentCase = Case.getCurrentCaseThrows(); SleuthkitCase tskDb = currentCase.getSleuthkitCase(); AbstractFile abstractFile = tskDb.findAllFilesWhere(String.format("obj_id in (%s)", file.getObjectId())).get(0); - + return new FileInstanceNode(abstractFile, file.getDataSourceName()); } catch (NoCurrentCaseException | TskCoreException ex) { LOGGER.log(Level.SEVERE, String.format("Unable to create node for file with obj_id: %s.", new Object[]{file.getObjectId()}), ex); @@ -168,7 +147,7 @@ public class Md5Node extends DisplayableItemNode { } @Override - protected boolean createKeys(List list) { + protected boolean createKeys(List list) { list.addAll(this.descendants.getMetadata()); return true; } @@ -195,5 +174,5 @@ public class Md5Node extends DisplayableItemNode { return this.displayString; } } - + } From 94ad39dd5900dfb577238faefc3c35cdeb8f1bdb Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Wed, 20 Jun 2018 18:12:59 -0700 Subject: [PATCH 05/21] Remove unused imports. --- .../autopsy/corecomponents/DataResultViewerTable.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 9863a98174..a9b7beaec2 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -44,7 +44,6 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ListSelectionEvent; import javax.swing.event.TableColumnModelEvent; import javax.swing.event.TableColumnModelListener; -import javax.swing.event.TreeExpansionEvent; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; @@ -56,7 +55,6 @@ import org.netbeans.swing.outline.DefaultOutlineModel; import org.netbeans.swing.outline.Outline; import org.openide.explorer.ExplorerManager; import org.openide.explorer.view.OutlineView; -import org.openide.explorer.view.Visualizer; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -64,13 +62,10 @@ import org.openide.nodes.Node.Property; import org.openide.util.NbBundle; import org.openide.util.NbPreferences; import org.openide.util.lookup.ServiceProvider; -import org.sleuthkit.autopsy.commonfilesearch.CommonFilesNode; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; -import org.sleuthkit.autopsy.datamodel.InstanceCountNode; import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; -import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; /** * A tabular result viewer that displays the children of the given root node @@ -200,6 +195,7 @@ public final class DataResultViewerTable extends AbstractDataResultViewer { /** * Gets the title of this tabular result viewer. + * @return */ @Override @NbBundle.Messages("DataResultViewerTable.title=Table") From 332a174b0a63b5022f2e6d981c59117b94c56b72 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Wed, 20 Jun 2018 19:49:28 -0700 Subject: [PATCH 06/21] remove excess comments. --- .../DelayedLoadChildNodesOnTreeExpansion.java | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java index bdc7e4b7c7..8dd71af77d 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java @@ -36,16 +36,10 @@ public final class DelayedLoadChildNodesOnTreeExpansion implements TreeExpansion * could trigger collapsing and (re-)expanding nodes again. * @param event */ - //private boolean inRecursion = false; @Override public synchronized void treeCollapsed(final TreeExpansionEvent event) { - Node eventNode = Visualizer.findNode(event.getPath().getLastPathComponent()); -// if (eventNode instanceof CommonFilesNode) { // avoid endless //!inRecursion && -// // recursion -// final CommonFilesNode node = (CommonFilesNode) eventNode; -// node.setCleanRefreshNeeded(true); -// } + } @Override @@ -54,16 +48,6 @@ public final class DelayedLoadChildNodesOnTreeExpansion implements TreeExpansion if (eventNode instanceof MultiLayerTableFilterNode) { //!inRecursion && final MultiLayerTableFilterNode node = (MultiLayerTableFilterNode) eventNode; node.refresh(); -// if (!outlineView.isExpanded(node)) { -// // Seems that the refresh caused to collapse, re-expand again and -// // avoid recursion in this listener! -// inRecursion = true; -// try { -// outlineView.expandNode(node); -// } finally { -// inRecursion = false; -// } -// } } } From 987eb9a63b6272c75eb0e4438963b742c9408963 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Thu, 21 Jun 2018 11:04:10 -0700 Subject: [PATCH 07/21] Remove unused, commented out code. --- .../sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java | 1 - Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java | 1 - 2 files changed, 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java index 90995ca00d..0341662ffa 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.commonfilesearch; import java.util.Collections; -import java.util.HashMap; import java.util.Map; /** diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java index 9283de6026..97cd1b112d 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java @@ -67,7 +67,6 @@ final public class InstanceCountNode extends DisplayableItemNode { public void refresh() { metadataList.displayDelayedMetadata(); - //firePropertyChange(PROP_NAME, this, this); setChildren(Children.create(new Md5NodeFactory(metadataList.getMetadataList()), false)); } From 5281c381523c63f6e90fe5bb34514795930bc452 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Thu, 21 Jun 2018 11:06:11 -0700 Subject: [PATCH 08/21] more comment removal cleanup. --- Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java | 2 +- Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java index 97cd1b112d..3389284d20 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java @@ -39,7 +39,7 @@ final public class InstanceCountNode extends DisplayableItemNode { final private Md5MetadataList metadataList; public InstanceCountNode(int instanceCount, Md5MetadataList md5Metadata) { - super(Children.create(new Md5NodeFactory(md5Metadata.getMetadataList()), true)); //Children.createLazy(new InstanceCountChildCallable(md5Metadata)), Lookups.singleton(instanceCount)); + super(Children.create(new Md5NodeFactory(md5Metadata.getMetadataList()), true)); this.instanceCount = instanceCount; this.metadataList = md5Metadata; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java b/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java index 9ebc81a076..b5092432a4 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java @@ -52,7 +52,7 @@ public class Md5Node extends DisplayableItemNode { private final String dataSources; public Md5Node(Md5Metadata data) { - super(Children.create(new FileInstanceNodeFactory(data), true)); //Children.createLazy(new Md5ChildCallable(data)), Lookups.singleton(data.getMd5())); + super(Children.create(new FileInstanceNodeFactory(data), true)); this.commonFileCount = data.size(); this.dataSources = String.join(", ", data.getDataSources()); From 80373241894b0354f61bc665d3002badbe65d3d5 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Thu, 21 Jun 2018 15:12:53 -0700 Subject: [PATCH 09/21] Swap reflection for lookups instead. --- .../corecomponents/MultiLayerTableFilterNode.java | 12 +++++------- .../autopsy/directorytree/DataResultFilterNode.java | 9 +++------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiLayerTableFilterNode.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiLayerTableFilterNode.java index b5399ca2bc..53f705b7b4 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiLayerTableFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiLayerTableFilterNode.java @@ -36,7 +36,7 @@ public class MultiLayerTableFilterNode extends FilterNode implements TableFilter private final boolean createChildren; private final boolean forceUseWrappedDisplayName; private static final String columnOrderKey = "NONE"; - private final Node innerNode; + //private final Node innerNode; /** * Constructs a filter node that generates children using @@ -50,14 +50,12 @@ public class MultiLayerTableFilterNode extends FilterNode implements TableFilter super(node, TableFilterChildrenWithDescendants.createInstance(node, true, childLayerDepth), Lookups.proxy(node)); this.createChildren = true; this.forceUseWrappedDisplayName = true; - this.innerNode = node; + //this.innerNode = node; } - void refresh() { - if (innerNode instanceof DataResultFilterNode) { - final DataResultFilterNode drNode = (DataResultFilterNode) innerNode; - drNode.refresh(); - } + void refresh() { + DataResultFilterNode innerNode = getLookup().lookup(DataResultFilterNode.class); + innerNode.refresh(); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index ed0cf41bae..00da91813c 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -98,7 +98,6 @@ public class DataResultFilterNode extends FilterNode { private static boolean filterKnownFromViews = UserPreferences.hideKnownFilesInViewsTree(); private static boolean filterSlackFromDataSources = UserPreferences.hideSlackFilesInDataSourcesTree(); private static boolean filterSlackFromViews = UserPreferences.hideSlackFilesInViewsTree(); - private Node innerNode; static { UserPreferences.addChangeListener(new PreferenceChangeListener() { @@ -139,13 +138,12 @@ public class DataResultFilterNode extends FilterNode { public DataResultFilterNode(Node node, ExplorerManager em) { super(node, new DataResultFilterChildren(node, em)); this.sourceEm = em; - this.innerNode = node; } public void refresh() { - if (innerNode instanceof InstanceCountNode) { - final InstanceCountNode cfNode = (InstanceCountNode) innerNode; - cfNode.refresh(); + if (getOriginal() instanceof InstanceCountNode) { + InstanceCountNode innerNode = getLookup().lookup(InstanceCountNode.class); + innerNode.refresh(); } } @@ -165,7 +163,6 @@ public class DataResultFilterNode extends FilterNode { private DataResultFilterNode(Node node, ExplorerManager em, boolean filterKnown, boolean filterSlack) { super(node, new DataResultFilterChildren(node, em, filterKnown, filterSlack)); this.sourceEm = em; - this.innerNode = node; } /** From 4d3fc1f23d5748b914afd4ac0c2f284b82e9f09a Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Mon, 6 Aug 2018 14:10:22 -0700 Subject: [PATCH 10/21] Fix instance count --- .../autopsy/commonfilesearch/CommonAttributeValueList.java | 4 ++++ .../sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java index 6f9d7d796e..f86f4c23ff 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java @@ -52,6 +52,10 @@ final public class CommonAttributeValueList { public List getMetadataList() { return Collections.unmodifiableList(this.metadataList); } + + public int getCommonAttributeListSize() { + return this.delayedMetadataList.size(); + } public void displayDelayedMetadata() { if (metadataList.isEmpty()) { diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java index da418c6213..878b51bdbc 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java @@ -57,7 +57,7 @@ final public class InstanceCountNode extends DisplayableItemNode { this.instanceCount = instanceCount; this.attributeValues = attributeValues; - this.setDisplayName(String.format(Bundle.InstanceCountNode_displayName(), Integer.toString(instanceCount), attributeValues.getMetadataList().size())); + this.setDisplayName(String.format(Bundle.InstanceCountNode_displayName(), Integer.toString(instanceCount), attributeValues.getCommonAttributeListSize())); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS } From 7df29a843c06fab8ede271dfe8dfb45f40a73e50 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Fri, 10 Aug 2018 11:26:01 -0700 Subject: [PATCH 11/21] Remove comment --- .../corecomponents/DelayedLoadChildNodesOnTreeExpansion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java index dc47eba7cc..a66f4a876a 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java @@ -45,7 +45,7 @@ public final class DelayedLoadChildNodesOnTreeExpansion implements TreeExpansion @Override public synchronized void treeExpanded(final TreeExpansionEvent event) { Node eventNode = Visualizer.findNode(event.getPath().getLastPathComponent()); - if (eventNode instanceof TableFilterNode) { //!inRecursion && + if (eventNode instanceof TableFilterNode) { final TableFilterNode node = (TableFilterNode) eventNode; node.refresh(); } From adc5e9c53b8beb140ad4dd411168cdc91287daf5 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Fri, 17 Aug 2018 12:17:33 -0700 Subject: [PATCH 12/21] Fix columns, add swing worker to prevent complete UI lockup on refresh() --- ...monAttributesSearchResultsViewerTable.java | 2 +- .../commonfilesearch/InstanceCountNode.java | 59 ++++++++++++++----- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java index 5630c56b89..0e051d5111 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java @@ -56,7 +56,7 @@ public class CommonAttributesSearchResultsViewerTable extends DataResultViewerTa map.put(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), 200); map.put(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), 100); map.put(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), 130); - map.put(Bundle.CommonFilesSearchResultsViewerTable_tagsColLbl1(), 300);; + map.put(Bundle.CommonFilesSearchResultsViewerTable_tagsColLbl1(), 300); COLUMN_WIDTHS = Collections.unmodifiableMap(map); } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java index 878b51bdbc..b662102fc9 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java @@ -19,34 +19,40 @@ */ package org.sleuthkit.autopsy.commonfilesearch; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import javax.swing.SwingWorker; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.NodeProperty; /** - * Node used to indicate the number of matches found with the MD5 children - * of this Node. + * Node used to indicate the number of matches found with the MD5 children of + * this Node. */ final public class InstanceCountNode extends DisplayableItemNode { + private static final Logger logger = Logger.getLogger(InstanceCountNode.class.getName()); + final private int instanceCount; final private CommonAttributeValueList attributeValues; /** - * Create a node with the given number of instances, and the given - * selection of metadata. + * Create a node with the given number of instances, and the given selection + * of metadata. + * * @param instanceCount - * @param attributeValues + * @param attributeValues */ @NbBundle.Messages({ "InstanceCountNode.displayName=Files with %s instances (%s)" @@ -56,26 +62,45 @@ final public class InstanceCountNode extends DisplayableItemNode { this.instanceCount = instanceCount; this.attributeValues = attributeValues; - + this.setDisplayName(String.format(Bundle.InstanceCountNode_displayName(), Integer.toString(instanceCount), attributeValues.getCommonAttributeListSize())); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS } /** * Number of matches found for each of the MD5 children. + * * @return int match count */ int getInstanceCount() { return this.instanceCount; } - + public void refresh() { - attributeValues.displayDelayedMetadata(); - setChildren(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList()), false)); + new SwingWorker() { + + @Override + protected Void doInBackground() throws Exception { + attributeValues.displayDelayedMetadata(); + setChildren(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList()), false)); + return null; + } + + @Override + protected void done() { + super.done(); + try { + get(); + } catch (InterruptedException | ExecutionException ex) { + logger.log(Level.SEVERE, "Unexpected exception while loading common search result instances", ex); //NON-NLS + } + } + }.execute(); } /** * Get a list of metadata for the MD5s which are children of this object. + * * @return List */ CommonAttributeValueList getAttributeValues() { @@ -106,14 +131,20 @@ final public class InstanceCountNode extends DisplayableItemNode { sheetSet = Sheet.createPropertiesSet(); sheet.put(sheetSet); } - + final String NO_DESCR = Bundle.InstanceCountNode_createSheet_noDescription(); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, "")); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), NO_DESCR, this.getInstanceCount())); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_tagsColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_tagsColLbl1(), NO_DESCR, "")); + return sheet; } - /** * ChildFactory which builds CommonFileParentNodes from the * CommonFilesMetaaData models. @@ -143,11 +174,11 @@ final public class InstanceCountNode extends DisplayableItemNode { list.addAll(this.metadata.keySet()); return true; } - + @Override protected Node createNodeForKey(String attributeValue) { CommonAttributeValue md5Metadata = this.metadata.get(attributeValue); return new CommonAttributeValueNode(md5Metadata); } } -} \ No newline at end of file +} From f4c253f1ae384a37c46466734d3b94215dc02395 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Mon, 27 Aug 2018 16:34:50 -0400 Subject: [PATCH 13/21] Removed a duplicate scan of the output directory for each manifest file --- .../autopsy/experimental/autoingest/AutoIngestManager.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index 8dddd2a3e6..ebf1bda7b8 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -1431,10 +1431,6 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen sysLogger.log(Level.SEVERE, String.format("Error attempting to set node data for %s", manifest.getFilePath()), ex); } } - Path caseDirectory = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName()); - if (null != caseDirectory) { - job.setCaseDirectoryPath(caseDirectory); - } newPendingJobsList.add(job); } From 46bb667bf3dbb8fb99b9fa219dada4aca42c0eb8 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Mon, 27 Aug 2018 16:52:26 -0400 Subject: [PATCH 14/21] No longer trying to find case output directory for each job while doing an input scan --- .../autoingest/AutoIngestManager.java | 80 +++++++++---------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index ebf1bda7b8..6239be509b 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -1398,10 +1398,6 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen AutoIngestJob job; if (nodeData.getVersion() == AutoIngestJobNodeData.getCurrentVersion()) { job = new AutoIngestJob(nodeData); - Path caseDirectory = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName()); - if (null != caseDirectory) { - job.setCaseDirectoryPath(caseDirectory); - } } else { job = new AutoIngestJob(manifest); job.setPriority(nodeData.getPriority()); // Retain priority, present in all versions of the node data. @@ -1591,54 +1587,53 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * @throws InterruptedException */ private void addCompletedJob(Manifest manifest, AutoIngestJobNodeData nodeData) throws CoordinationServiceException, InterruptedException, AutoIngestJobException { - Path caseDirectoryPath = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName()); - if (null != caseDirectoryPath) { - AutoIngestJob job; - if (nodeData.getVersion() == AutoIngestJobNodeData.getCurrentVersion()) { - job = new AutoIngestJob(nodeData); - job.setCaseDirectoryPath(caseDirectoryPath); - } else { - /** - * Use the manifest rather than the node data here to create - * a new AutoIngestJob instance because the AutoIngestJob - * constructor that takes a node data object expects the - * node data to have fields that do not exist in earlier - * versions. - */ - job = new AutoIngestJob(manifest); - job.setCaseDirectoryPath(caseDirectoryPath); + Path caseDirectoryPath = nodeData.getCaseDirectoryPath(); + if (!caseDirectoryPath.toFile().exists()) { + sysLogger.log(Level.WARNING, String.format("Job completed for %s, but cannot find case directory, ignoring job", nodeData.getManifestFilePath())); + return; + } - /** - * Update the job with the fields that exist in all versions - * of the nodeData. - */ - job.setCompletedDate(nodeData.getCompletedDate()); - job.setErrorsOccurred(nodeData.getErrorsOccurred()); - job.setPriority(nodeData.getPriority()); - job.setNumberOfCrashes(nodeData.getNumberOfCrashes()); - job.setProcessingStage(AutoIngestJob.Stage.COMPLETED, nodeData.getCompletedDate()); - job.setProcessingStatus(AutoIngestJob.ProcessingStatus.COMPLETED); + AutoIngestJob job; + if (nodeData.getVersion() == AutoIngestJobNodeData.getCurrentVersion()) { + job = new AutoIngestJob(nodeData); + job.setCaseDirectoryPath(caseDirectoryPath); + } else { + /** + * Use the manifest rather than the node data here to create a + * new AutoIngestJob instance because the AutoIngestJob + * constructor that takes a node data object expects the node + * data to have fields that do not exist in earlier versions. + */ + job = new AutoIngestJob(manifest); + job.setCaseDirectoryPath(caseDirectoryPath); - /* + /** + * Update the job with the fields that exist in all versions of + * the nodeData. + */ + job.setCompletedDate(nodeData.getCompletedDate()); + job.setErrorsOccurred(nodeData.getErrorsOccurred()); + job.setPriority(nodeData.getPriority()); + job.setNumberOfCrashes(nodeData.getNumberOfCrashes()); + job.setProcessingStage(AutoIngestJob.Stage.COMPLETED, nodeData.getCompletedDate()); + job.setProcessingStatus(AutoIngestJob.ProcessingStatus.COMPLETED); + + /* * Try to upgrade/update the coordination service manifest * node data for the job. It is possible that two hosts will * both try to obtain the lock to do the upgrade operation * at the same time. If this happens, the host that is * holding the lock will complete the upgrade operation. - */ - try (Lock manifestLock = coordinationService.tryGetExclusiveLock(CoordinationService.CategoryNode.MANIFESTS, manifest.getFilePath().toString())) { - if (null != manifestLock) { - updateCoordinationServiceManifestNode(job); - } - } catch (CoordinationServiceException ex) { - sysLogger.log(Level.SEVERE, String.format("Error attempting to set node data for %s", manifest.getFilePath()), ex); + */ + try (Lock manifestLock = coordinationService.tryGetExclusiveLock(CoordinationService.CategoryNode.MANIFESTS, manifest.getFilePath().toString())) { + if (null != manifestLock) { + updateCoordinationServiceManifestNode(job); } + } catch (CoordinationServiceException ex) { + sysLogger.log(Level.SEVERE, String.format("Error attempting to set node data for %s", manifest.getFilePath()), ex); } - newCompletedJobsList.add(job); - - } else { - sysLogger.log(Level.WARNING, String.format("Job completed for %s, but cannot find case directory, ignoring job", nodeData.getManifestFilePath())); } + newCompletedJobsList.add(job); } /** @@ -2452,6 +2447,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen Thread.sleep(AutoIngestUserPreferences.getSecondsToSleepBetweenCases() * 1000); } currentJob.setCaseDirectoryPath(caseDirectoryPath); + updateCoordinationServiceManifestNode(currentJob); // update case directory path Case caseForJob = Case.getCurrentCase(); sysLogger.log(Level.INFO, "Opened case {0} for {1}", new Object[]{caseForJob.getName(), manifest.getFilePath()}); return caseForJob; From b5dcd2bd37862c5d18d5981463d37ea0657c21e8 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Tue, 28 Aug 2018 10:37:07 -0400 Subject: [PATCH 15/21] Additional logging --- .../autopsy/experimental/autoingest/AutoIngestManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index 6239be509b..e8500e6ba2 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -1589,7 +1589,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen private void addCompletedJob(Manifest manifest, AutoIngestJobNodeData nodeData) throws CoordinationServiceException, InterruptedException, AutoIngestJobException { Path caseDirectoryPath = nodeData.getCaseDirectoryPath(); if (!caseDirectoryPath.toFile().exists()) { - sysLogger.log(Level.WARNING, String.format("Job completed for %s, but cannot find case directory, ignoring job", nodeData.getManifestFilePath())); + sysLogger.log(Level.WARNING, String.format("Job completed for %s, but cannot find case directory %s, ignoring job", nodeData.getManifestFilePath(), caseDirectoryPath.toString())); return; } From 120b3db105afc799bf5bccc7e8fe05c15cae6d65 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 28 Aug 2018 11:37:19 -0600 Subject: [PATCH 16/21] comparison was backwards --- .../commonfilesearch/CommonAttributeSearchResults.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java index e8efefa6f4..388dca1bab 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java @@ -86,13 +86,13 @@ final public class CommonAttributeSearchResults { * search. * * Remove results which are not found in the portion of available data - * sources described by minimumPercentageThreshold. + sources described by maximumPercentageThreshold. * * @return metadata */ - private Map> getMetadata(int minimumPercentageThreshold) throws EamDbException { + private Map> getMetadata(int maximumPercentageThreshold) throws EamDbException { - if(minimumPercentageThreshold == 0){ + if(maximumPercentageThreshold == 0){ return Collections.unmodifiableMap(this.instanceCountToAttributeValues); } @@ -115,7 +115,7 @@ final public class CommonAttributeSearchResults { int frequencyPercentage = eamDb.getFrequencyPercentage(new CorrelationAttributeInstance(fileAttributeType, value.getValue())); - if(frequencyPercentage < minimumPercentageThreshold){ + if(frequencyPercentage > maximumPercentageThreshold){ if(itemsToRemove.containsKey(key)){ itemsToRemove.get(key).add(value); } else { From ea29efccf3aa8cd6a8de0eea88f6f7ff77af675f Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Tue, 28 Aug 2018 13:24:03 -0700 Subject: [PATCH 17/21] Address PR feedback --- .../CommonAttributeValueList.java | 32 +++++++++++++++++-- .../commonfilesearch/InstanceCountNode.java | 31 +++++------------- .../DelayedLoadChildNodesOnTreeExpansion.java | 2 +- .../corecomponents/TableFilterNode.java | 4 +++ .../directorytree/DataResultFilterNode.java | 5 +++ .../commonfilessearch/InterCaseTestUtils.java | 7 ++-- .../commonfilessearch/IntraCaseTestUtils.java | 6 ++-- 7 files changed, 56 insertions(+), 31 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java index f86f4c23ff..4797dbfd94 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java @@ -30,7 +30,15 @@ import java.util.List; */ final public class CommonAttributeValueList { + /** + * The list of value nodes, which begins empty. + */ private final List metadataList; + + /** + * The backing list of value nodes, which will be dynamically loaded + * when requested. + */ private final List delayedMetadataList; /** @@ -49,21 +57,41 @@ final public class CommonAttributeValueList { this.delayedMetadataList = new ArrayList<>(); } + /** + * Get the list of value nodes. Will be empty if + * displayDelayedMetadata() has not been called for the + * parent InstanceCountNode + * @return metadataList the list of nodes + */ public List getMetadataList() { return Collections.unmodifiableList(this.metadataList); } - public int getCommonAttributeListSize() { + /** + * Return the size of the backing list, in case + * displayDelayedMetadata() has not be called yet. + * @return int the number of matches for this value + */ + int getCommonAttributeListSize() { return this.delayedMetadataList.size(); } + /** + * Dynamically load the list CommonAttributeValue when called. Until called + * metadataList should be empty. The parent node, InstanceCountNode, will + * trigger the factory call and refresh. + */ public void displayDelayedMetadata() { if (metadataList.isEmpty()) { this.metadataList.addAll(this.delayedMetadataList); } } - public void addMetadataToList(CommonAttributeValue metadata) { + /** + * A a value node to the list, to be loaded later. + * @param metadata the node to add + */ + void addMetadataToList(CommonAttributeValue metadata) { delayedMetadataList.add(metadata); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java index b662102fc9..fa298c121d 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java @@ -23,9 +23,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.logging.Level; -import javax.swing.SwingWorker; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -76,26 +73,14 @@ final public class InstanceCountNode extends DisplayableItemNode { return this.instanceCount; } + /** + * Refresh the node, by dynamically loading in the children when called, and + * calling the CommonAttributeValueNodeFactory to generate nodes for the + * children in attributeValues. + */ public void refresh() { - new SwingWorker() { - - @Override - protected Void doInBackground() throws Exception { - attributeValues.displayDelayedMetadata(); - setChildren(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList()), false)); - return null; - } - - @Override - protected void done() { - super.done(); - try { - get(); - } catch (InterruptedException | ExecutionException ex) { - logger.log(Level.SEVERE, "Unexpected exception while loading common search result instances", ex); //NON-NLS - } - } - }.execute(); + attributeValues.displayDelayedMetadata(); + setChildren(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList()), true)); } /** @@ -147,7 +132,7 @@ final public class InstanceCountNode extends DisplayableItemNode { /** * ChildFactory which builds CommonFileParentNodes from the - * CommonFilesMetaaData models. + * CommonAttributeValue metadata models. */ static class CommonAttributeValueNodeFactory extends ChildFactory { diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java index a66f4a876a..6e6d28af2a 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java @@ -39,7 +39,7 @@ public final class DelayedLoadChildNodesOnTreeExpansion implements TreeExpansion @Override public synchronized void treeCollapsed(final TreeExpansionEvent event) { - + // Do nothing on collapse. Netbeans should manage nodes falling out of scope and GC. } @Override diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterNode.java b/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterNode.java index 890fb4ace3..2ba02f694a 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterNode.java @@ -129,6 +129,10 @@ public class TableFilterNode extends FilterNode { } } + /** + * Refreshes the inner node, which depending on the actual node type that was wrapped + * could trigger a dynamic refresh of the children, if supported. + */ void refresh() { DataResultFilterNode innerNode = getLookup().lookup(DataResultFilterNode.class); innerNode.refresh(); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index c639a96672..c94ad57094 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -140,6 +140,11 @@ public class DataResultFilterNode extends FilterNode { this.sourceEm = em; } + /** + * Refreshes the inner node. If the actual underlying node is an InstanceCountNode, + * refresh() that node, which refreshes the children. + * + */ public void refresh() { if (getOriginal() instanceof InstanceCountNode) { InstanceCountNode innerNode = getLookup().lookup(InstanceCountNode.class); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java index cf6bd36fbc..0feb32ee4c 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java @@ -57,6 +57,7 @@ import org.sleuthkit.autopsy.commonfilesearch.CentralRepoCommonAttributeInstance import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; import org.sleuthkit.autopsy.commonfilesearch.DataSourceLoader; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValue; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValueList; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.datamodel.AbstractFile; @@ -309,9 +310,9 @@ class InterCaseTestUtils { int tally = 0; - for(Map.Entry> entry : searchDomain.getMetadata().entrySet()){ - - for(CommonAttributeValue value : entry.getValue()){ + for(Map.Entry entry : searchDomain.getMetadata().entrySet()){ + entry.getValue().displayDelayedMetadata(); + for(CommonAttributeValue value : entry.getValue().getMetadataList()) { for(AbstractCommonAttributeInstance commonAttribute : value.getInstances()){ diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java index 82c38cdf56..b03e6b2dfa 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java @@ -37,6 +37,7 @@ import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeInstance; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; import org.sleuthkit.autopsy.commonfilesearch.DataSourceLoader; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValue; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValueList; import org.sleuthkit.autopsy.testutils.CaseUtils; import org.sleuthkit.autopsy.testutils.IngestUtils; import org.sleuthkit.datamodel.AbstractFile; @@ -205,8 +206,9 @@ class IntraCaseTestUtils { static Map mapFileInstancesToDataSources(CommonAttributeSearchResults metadata) { Map instanceIdToDataSource = new HashMap<>(); - for (Map.Entry> entry : metadata.getMetadata().entrySet()) { - for (CommonAttributeValue md : entry.getValue()) { + for (Map.Entry entry : metadata.getMetadata().entrySet()) { + entry.getValue().displayDelayedMetadata(); + for (CommonAttributeValue md : entry.getValue().getMetadataList()) { for (AbstractCommonAttributeInstance fim : md.getInstances()) { instanceIdToDataSource.put(fim.getAbstractFileObjectId(), fim.getDataSource()); } From e5f1936e1f0345272bfda1cbee1063cc301ff93b Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Tue, 28 Aug 2018 15:10:51 -0700 Subject: [PATCH 18/21] Make outlineView protected so that CommonAttributesSearchResultsViewerTable can add the treelistener itself. --- ...ommonAttributesSearchResultsViewerTable.java | 12 ++++++++++-- .../corecomponents/DataResultViewerTable.form | 1 + .../corecomponents/DataResultViewerTable.java | 17 ++--------------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java index 0e051d5111..97a41b27be 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java @@ -60,9 +60,17 @@ public class CommonAttributesSearchResultsViewerTable extends DataResultViewerTa COLUMN_WIDTHS = Collections.unmodifiableMap(map); } - + /** + * Implements a DataResultViewerTable which constructs a tabular result viewer that + * displays the children of the given root node using an OutlineView. The explorer + * manager will be discovered at runtime. + * + * Adds a TreeExpansionsListener to the outlineView to receive tree expansion events + * which dynamically loads children nodes when requested. + */ public CommonAttributesSearchResultsViewerTable() { - super(new DelayedLoadChildNodesOnTreeExpansion()); + super(); + outlineView.addTreeExpansionListener(new DelayedLoadChildNodesOnTreeExpansion()); } @NbBundle.Messages({ diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form index d6c32623a4..8ad47b32c7 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form @@ -29,6 +29,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 1ef52568e1..024686e26f 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -47,7 +47,6 @@ import javax.swing.event.TableColumnModelListener; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; -import javax.swing.event.TreeExpansionListener; import org.netbeans.swing.etable.ETableColumn; import org.netbeans.swing.etable.ETableColumnModel; import org.netbeans.swing.outline.DefaultOutlineCellRenderer; @@ -103,20 +102,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { public DataResultViewerTable() { this(null, Bundle.DataResultViewerTable_title()); } - /** - * Constructs a tabular result viewer that displays the children of the - * given root node using an OutlineView. The viewer should have an ancestor - * top component to connect the lookups of the nodes displayed in the - * OutlineView to the actions global context. The explorer manager will be - * discovered at runtime. - * @param listener - */ - public DataResultViewerTable(TreeExpansionListener listener) { - this(null, Bundle.DataResultViewerTable_title()); - outlineView.addTreeExpansionListener(listener); - } - /** * Constructs a tabular result viewer that displays the children of a given @@ -201,6 +187,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { public String getTitle() { return title; } + /** * Indicates whether a given node is supported as a root node for this @@ -852,7 +839,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { ); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables - private org.openide.explorer.view.OutlineView outlineView; + protected org.openide.explorer.view.OutlineView outlineView; // End of variables declaration//GEN-END:variables } From c202db7261e24a526969906d783c8b538f84b343 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 28 Aug 2018 17:49:46 -0600 Subject: [PATCH 19/21] error messages truncated --- .../commonfilesearch/Bundle.properties | 14 +- .../CommonAttributePanel.form | 137 +++++++----- .../CommonAttributePanel.java | 195 ++++++++++-------- .../UserInputErrorManager.java | 2 +- 4 files changed, 206 insertions(+), 142 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties index 8864d4a73a..b8f2e3e0d7 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties @@ -9,10 +9,14 @@ IntraCasePanel.withinDataSourceRadioButton.text=At least one match must appear i IntraCasePanel.selectDataSourceComboBox.actionCommand= InterCasePanel.specificCentralRepoCaseRadio.text=Matches must be from the following Central Repo case: InterCasePanel.anyCentralRepoCaseRadio.text=Matches may be from any Central Repo case -CommonAttributePanel.commonFilesSearchLabel2.text=Scope of Search +CommonAttributePanel.jCheckBox1.text=Hide files found in over +CommonAttributePanel.jLabel1.text=% of data sources in central repository. +CommonAttributePanel.percentageThresholdTextTwo.text_1=% of data sources in central repository. +CommonAttributePanel.percentageThresholdTextOne.text=20 +CommonAttributePanel.percentageThresholdCheck.text_1=Hide files found in over CommonAttributePanel.intraCaseRadio.text=Within current case CommonAttributePanel.commonFilesSearchLabel1.text=Find common files to correlate data soures or cases. -CommonAttributePanel.errorText.text=In order to search, you must select a file category. +CommonAttributePanel.errorText.text=In order to search, you must select a file category. CommonAttributePanel.categoriesLabel.text=File Types To Include: CommonAttributePanel.documentsCheckbox.text=Documents CommonAttributePanel.pictureVideoCheckbox.text=Pictures and Videos @@ -23,8 +27,4 @@ CommonAttributePanel.allFileCategoriesRadioButton.text=All file types CommonAttributePanel.cancelButton.actionCommand=Cancel CommonAttributePanel.cancelButton.text=Cancel CommonAttributePanel.searchButton.text=Search -CommonAttributePanel.jCheckBox1.text=Hide files found in over -CommonAttributePanel.jLabel1.text=% of data sources in central repository. -CommonAttributePanel.percentageThreshold.text=20 -CommonAttributePanel.jLabel1.text_1=% of data sources in central repository. -CommonAttributePanel.percentageThresholdCheck.text_1=Hide files found in over +CommonAttributePanel.commonFilesSearchLabel2.text=Scope of Search diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form index 329de08aef..20cbb33fe9 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form @@ -8,8 +8,11 @@ + + + - + @@ -29,7 +32,7 @@ - + @@ -37,14 +40,15 @@ - + - + - + + @@ -55,45 +59,57 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -123,16 +139,22 @@ - - + + - - - - + + + + + + + + + + - + @@ -243,6 +265,7 @@ + @@ -310,10 +333,10 @@ - + - + @@ -326,13 +349,33 @@ - + - + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index f9a5c114bc..1955b17ac9 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -84,7 +84,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { initComponents(); this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); - this.errorText.setVisible(false); + //this.errorText.setVisible(false); this.setupDataSources(); if (CommonAttributePanel.isEamDbAvailableForIntercaseSearch()) { @@ -101,12 +101,12 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.errorManager = new UserInputErrorManager(); - this.percentageThreshold.getDocument().addDocumentListener(new DocumentListener(){ + this.percentageThresholdTextOne.getDocument().addDocumentListener(new DocumentListener(){ - private Dimension preferredSize = CommonAttributePanel.this.percentageThreshold.getPreferredSize(); + private Dimension preferredSize = CommonAttributePanel.this.percentageThresholdTextOne.getPreferredSize(); private void maintainSize(){ - CommonAttributePanel.this.percentageThreshold.setSize(preferredSize); + CommonAttributePanel.this.percentageThresholdTextOne.setSize(preferredSize); } @Override @@ -466,10 +466,13 @@ public final class CommonAttributePanel extends javax.swing.JDialog { intraCasePanel = new org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel(); interCasePanel = new org.sleuthkit.autopsy.commonfilesearch.InterCasePanel(); percentageThresholdCheck = new javax.swing.JCheckBox(); - percentageThreshold = new javax.swing.JTextField(); - jLabel1 = new javax.swing.JLabel(); + percentageThresholdTextOne = new javax.swing.JTextField(); + percentageThresholdTextTwo = new javax.swing.JLabel(); + filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); + filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 32767)); - setMinimumSize(new java.awt.Dimension(412, 375)); + setMaximumSize(new java.awt.Dimension(450, 375)); + setMinimumSize(new java.awt.Dimension(450, 375)); setResizable(false); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosed(java.awt.event.WindowEvent evt) { @@ -477,9 +480,10 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } }); - jPanel1.setMaximumSize(new java.awt.Dimension(412, 375)); - jPanel1.setMinimumSize(new java.awt.Dimension(412, 375)); - jPanel1.setPreferredSize(new java.awt.Dimension(412, 375)); + jPanel1.setMaximumSize(new java.awt.Dimension(450, 375)); + jPanel1.setMinimumSize(new java.awt.Dimension(450, 375)); + jPanel1.setPreferredSize(new java.awt.Dimension(450, 375)); + jPanel1.setRequestFocusEnabled(false); org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel2, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel2.text")); // NOI18N commonFilesSearchLabel2.setFocusable(false); @@ -542,6 +546,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { errorText.setForeground(new java.awt.Color(255, 0, 0)); org.openide.awt.Mnemonics.setLocalizedText(errorText, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.errorText.text")); // NOI18N + errorText.setVerticalAlignment(javax.swing.SwingConstants.TOP); org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel1.text")); // NOI18N commonFilesSearchLabel1.setFocusable(false); @@ -574,12 +579,12 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } }); - percentageThreshold.setText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThreshold.text")); // NOI18N - percentageThreshold.setMaximumSize(new java.awt.Dimension(40, 24)); - percentageThreshold.setMinimumSize(new java.awt.Dimension(40, 24)); - percentageThreshold.setPreferredSize(new java.awt.Dimension(40, 24)); + percentageThresholdTextOne.setText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdTextOne.text")); // NOI18N + percentageThresholdTextOne.setMaximumSize(new java.awt.Dimension(40, 24)); + percentageThresholdTextOne.setMinimumSize(new java.awt.Dimension(40, 24)); + percentageThresholdTextOne.setPreferredSize(new java.awt.Dimension(40, 24)); - org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.jLabel1.text_1")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(percentageThresholdTextTwo, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdTextTwo.text_1")); // NOI18N javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); @@ -588,35 +593,43 @@ public final class CommonAttributePanel extends javax.swing.JDialog { .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(commonFilesSearchLabel2) - .addComponent(intraCaseRadio) - .addComponent(interCaseRadio) - .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(categoriesLabel) - .addComponent(selectedFileCategoriesButton) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(29, 29, 29) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(documentsCheckbox) - .addComponent(pictureVideoCheckbox))) - .addComponent(allFileCategoriesRadioButton) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() - .addGap(10, 10, 10) - .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(percentageThresholdCheck) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(percentageThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel1)) - .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() .addComponent(searchButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cancelButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(errorText))) - .addContainerGap()) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(80, 80, 80) + .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(errorText))) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(commonFilesSearchLabel2) + .addComponent(intraCaseRadio) + .addComponent(interCaseRadio) + .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(categoriesLabel) + .addComponent(selectedFileCategoriesButton) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(29, 29, 29) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(documentsCheckbox) + .addComponent(pictureVideoCheckbox))) + .addComponent(allFileCategoriesRadioButton) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addGap(10, 10, 10) + .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(percentageThresholdCheck) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(percentageThresholdTextOne, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(percentageThresholdTextTwo))) + .addContainerGap(9, Short.MAX_VALUE)))) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -644,68 +657,72 @@ public final class CommonAttributePanel extends javax.swing.JDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(percentageThresholdCheck) - .addComponent(percentageThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel1)) + .addComponent(percentageThresholdTextOne, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(percentageThresholdTextTwo)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(searchButton) - .addComponent(cancelButton) - .addComponent(errorText)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(searchButton) + .addComponent(cancelButton) + .addComponent(errorText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) ); getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents - private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed - search(); - SwingUtilities.windowForComponent(this).dispose(); - }//GEN-LAST:event_searchButtonActionPerformed - - private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed - SwingUtilities.windowForComponent(this).dispose(); - }//GEN-LAST:event_cancelButtonActionPerformed - - private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed - this.handleFileTypeCheckBoxState(); - }//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed - - private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed - this.handleFileTypeCheckBoxState(); - }//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed - - private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed - this.handleFileTypeCheckBoxState(); - }//GEN-LAST:event_pictureVideoCheckboxActionPerformed - - private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed - this.handleFileTypeCheckBoxState(); - }//GEN-LAST:event_documentsCheckboxActionPerformed - - private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed - ((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel); - }//GEN-LAST:event_intraCaseRadioActionPerformed - - private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed - ((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel); - }//GEN-LAST:event_interCaseRadioActionPerformed - private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed SwingUtilities.windowForComponent(this).dispose(); }//GEN-LAST:event_formWindowClosed private void percentageThresholdCheckActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_percentageThresholdCheckActionPerformed if (this.percentageThresholdCheck.isSelected()) { - this.percentageThreshold.setEnabled(true); + this.percentageThresholdTextOne.setEnabled(true); } else { - this.percentageThreshold.setEnabled(false); + this.percentageThresholdTextOne.setEnabled(false); } this.handleFrequencyPercentageState(); }//GEN-LAST:event_percentageThresholdCheckActionPerformed + private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed + ((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel); + }//GEN-LAST:event_interCaseRadioActionPerformed + + private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed + ((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel); + }//GEN-LAST:event_intraCaseRadioActionPerformed + + private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_documentsCheckboxActionPerformed + + private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_pictureVideoCheckboxActionPerformed + + private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed + + private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + SwingUtilities.windowForComponent(this).dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed + search(); + SwingUtilities.windowForComponent(this).dispose(); + }//GEN-LAST:event_searchButtonActionPerformed + private void percentageThresholdChanged(){ - String percentageString = this.percentageThreshold.getText(); + String percentageString = this.percentageThresholdTextOne.getText(); try { this.percentageThresholdValue = Integer.parseInt(percentageString); @@ -731,15 +748,17 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } private void enablePercentageOptions() { - this.percentageThreshold.setEnabled(true); + this.percentageThresholdTextOne.setEnabled(true); this.percentageThresholdCheck.setEnabled(true); this.percentageThresholdCheck.setSelected(true); + this.percentageThresholdTextTwo.setEnabled(true); } private void disablePercentageOptions() { - this.percentageThreshold.setEnabled(false); + this.percentageThresholdTextOne.setEnabled(false); this.percentageThresholdCheck.setEnabled(false); this.percentageThresholdCheck.setSelected(false); + this.percentageThresholdTextTwo.setEnabled(false); } private void handleFileTypeCheckBoxState() { @@ -791,16 +810,18 @@ public final class CommonAttributePanel extends javax.swing.JDialog { private javax.swing.JCheckBox documentsCheckbox; private javax.swing.JLabel errorText; private javax.swing.ButtonGroup fileTypeFilterButtonGroup; + private javax.swing.Box.Filler filler1; + private javax.swing.Box.Filler filler2; private org.sleuthkit.autopsy.commonfilesearch.InterCasePanel interCasePanel; private javax.swing.JRadioButton interCaseRadio; private javax.swing.ButtonGroup interIntraButtonGroup; private org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel intraCasePanel; private javax.swing.JRadioButton intraCaseRadio; - private javax.swing.JLabel jLabel1; private javax.swing.JPanel jPanel1; private java.awt.Panel layoutPanel; - private javax.swing.JTextField percentageThreshold; private javax.swing.JCheckBox percentageThresholdCheck; + private javax.swing.JTextField percentageThresholdTextOne; + private javax.swing.JLabel percentageThresholdTextTwo; private javax.swing.JCheckBox pictureVideoCheckbox; private javax.swing.JButton searchButton; private javax.swing.JRadioButton selectedFileCategoriesButton; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java index 5b3e806bd4..358891da38 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java @@ -45,7 +45,7 @@ class UserInputErrorManager { // and add them to the map. this.currentErrors = new HashMap<>(); - this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage("Frequency percentage must be greater than zero and less than or equal to 100.")); + this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage("Invalid Frequency Percentage: 0 < % < 100.")); this.currentErrors.put(NO_FILE_CATEGORIES_SELECTED_KEY, new ErrorMessage("No file categories are included in the search.")); } From 59efa9bf0e8003abcdcf16b1aa48e4b27f6e8db3 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 28 Aug 2018 17:53:30 -0600 Subject: [PATCH 20/21] removed dead code --- .../sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index 1955b17ac9..21f2b72624 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -84,7 +84,6 @@ public final class CommonAttributePanel extends javax.swing.JDialog { initComponents(); this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); - //this.errorText.setVisible(false); this.setupDataSources(); if (CommonAttributePanel.isEamDbAvailableForIntercaseSearch()) { From 333060bebb07eb7cd4d0654dbfba742ba5bbd5dd Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 28 Aug 2018 18:15:35 -0600 Subject: [PATCH 21/21] ui text added to bundles --- .../autopsy/commonfilesearch/UserInputErrorManager.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java index 358891da38..3e09e32193 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import org.openide.util.NbBundle; /** * Manager for present state of errors on the Common Files Search. @@ -39,14 +40,17 @@ class UserInputErrorManager { * of all known error states, retrieve error messages, and determine if * anything is in an error state. */ + @NbBundle.Messages({ + "UserInputErrorManager.frequency=Invalid Frequency Percentage: 0 < % < 100.", + "UserInputErrorManager.categories=No file categories are included in the search."}) UserInputErrorManager (){ //when new errors are needed for the dialog, define a key and a value // and add them to the map. this.currentErrors = new HashMap<>(); - this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage("Invalid Frequency Percentage: 0 < % < 100.")); - this.currentErrors.put(NO_FILE_CATEGORIES_SELECTED_KEY, new ErrorMessage("No file categories are included in the search.")); + this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage(Bundle.UserInputErrorManager_frequency())); + this.currentErrors.put(NO_FILE_CATEGORIES_SELECTED_KEY, new ErrorMessage(Bundle.UserInputErrorManager_categories())); } /**