diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesChildren.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesChildren.java deleted file mode 100644 index 4ed2e988c8..0000000000 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesChildren.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.List; -import org.openide.nodes.ChildFactory; -import org.openide.nodes.Node; -import org.sleuthkit.autopsy.datamodel.CommonFileParentNode; - -/** - * Makes nodes for common files search results. - */ -public final class CommonFilesChildren extends ChildFactory { - - private List metaDataList; - - public CommonFilesChildren(List theMetaDataList) { - super(); - this.metaDataList = theMetaDataList; - } - - protected void removeNotify() { - metaDataList = null; - } - - @Override - protected Node createNodeForKey(CommonFilesMetaData metaData) { - - return new CommonFileParentNode(metaData); - } - - @Override - protected boolean createKeys(List toPopulate) { - toPopulate.addAll(metaDataList); - return true; - } - - -} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesDescendants.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesDescendants.java deleted file mode 100644 index c3ba7cdee1..0000000000 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesDescendants.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * - * 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.List; -import java.util.Map; -import org.openide.nodes.ChildFactory; -import org.openide.nodes.Node; -import org.sleuthkit.autopsy.datamodel.CommonFileChildNode; -import org.sleuthkit.datamodel.AbstractFile; - -/** - * Child generator for FileNodes of CommonFileParentNodes - */ -public class CommonFilesDescendants extends ChildFactory { - - private final List descendants; - private Map dataSourceMap; - - public CommonFilesDescendants(List descendants, Map dataSourceMap){ - super(); - this.descendants = descendants; - this.dataSourceMap = dataSourceMap; - } - - @Override - protected Node createNodeForKey(AbstractFile file){ - - final String dataSource = this.dataSourceMap.get(file.getDataSourceObjectId()); - - return new CommonFileChildNode(file, dataSource); - } - - @Override - protected boolean createKeys(List list) { - list.addAll(this.descendants); - return true; - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetaData.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetaData.java index a565db907e..65db9d319d 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetaData.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetaData.java @@ -28,7 +28,7 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; /** - * Utility and wrapper around data required for Common Files Search results. + * Utility and wrapper model around data required for Common Files Search results. * Subclass this to implement different selections of files from the case. */ public class CommonFilesMetaData { diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetaDataBuilder.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetaDataBuilder.java new file mode 100644 index 0000000000..2f6f7a0414 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetaDataBuilder.java @@ -0,0 +1,121 @@ +/* + * + * 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.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * + * Generates a List when collateFiles() is called, which organizes + * AbstractFiles by md5 to prepare to display in viewer. + */ +class CommonFilesMetaDataBuilder { + + private final Long selectedDataSourceId; + private final Map dataSourceIdToNameMap; + private final String singleDataSourceWhereClause = "md5 in (select md5 from tsk_files where data_source_obj_id=%s and (known != 1 OR known IS NULL) GROUP BY md5 HAVING COUNT(*) > 1) AND data_source_obj_id=%s order by md5"; + private final String allDataSourcesWhereClause = "md5 in (select md5 from tsk_files where (known != 1 OR known IS NULL) GROUP BY md5 HAVING COUNT(*) > 1) order by md5"; + + CommonFilesMetaDataBuilder(Long dataSourceId, Map dataSourceIdMap) { + selectedDataSourceId = dataSourceId; + dataSourceIdToNameMap = dataSourceIdMap; + } + + private void addDataSource(Set dataSources, AbstractFile file, Map dataSourceIdToNameMap) { + long datasourceId = file.getDataSourceObjectId(); + String dataSourceName = dataSourceIdToNameMap.get(datasourceId); + dataSources.add(dataSourceName); + } + + /** + * Sorts files in selection into a parent/child hierarchy where actual files + * are nested beneath a parent node which represents the common match. + * + * @return returns a reference to itself for ease of use. + * @throws TskCoreException + */ + List collateFiles() throws TskCoreException, SQLException { + List metaDataModels = new ArrayList<>(); + Map> md5ToDataSourcesStringMap = new HashMap<>(); + + try { + List files = FindCommonFiles(); + + Map> parentNodes = new HashMap<>(); + + collateParentChildRelationships(files, parentNodes, md5ToDataSourcesStringMap); + for (String key : parentNodes.keySet()) { + metaDataModels.add(new CommonFilesMetaData(key, parentNodes.get(key), String.join(", ", md5ToDataSourcesStringMap.get(key)), dataSourceIdToNameMap)); + } + } catch (NoCurrentCaseException ex) { + Exceptions.printStackTrace(ex); + } + + return metaDataModels; + } + + private void collateParentChildRelationships(List files, Map> parentNodes, Map> md5ToDataSourcesStringMap) { + for (AbstractFile file : files) { + + String currentMd5 = file.getMd5Hash(); + + if (parentNodes.containsKey(currentMd5)) { + parentNodes.get(currentMd5).add(file); + Set currenDataSources = md5ToDataSourcesStringMap.get(currentMd5); + addDataSource(currenDataSources, file, dataSourceIdToNameMap); + md5ToDataSourcesStringMap.put(currentMd5, currenDataSources); + + } else { + List children = new ArrayList<>(); + Set dataSources = new HashSet<>(); + children.add(file); + parentNodes.put(currentMd5, children); + addDataSource(dataSources, file, dataSourceIdToNameMap); + md5ToDataSourcesStringMap.put(currentMd5, dataSources); + } + + } + } + + private List FindCommonFiles() throws TskCoreException, NoCurrentCaseException { + SleuthkitCase sleuthkitCase; + sleuthkitCase = Case.getOpenCase().getSleuthkitCase(); + String whereClause = allDataSourcesWhereClause; + if (selectedDataSourceId != 0L) { + Object[] args = new String[]{Long.toString(selectedDataSourceId), Long.toString(selectedDataSourceId)}; + whereClause = String.format(singleDataSourceWhereClause, args); + } + List files = sleuthkitCase.findAllFilesWhere(whereClause); + return files; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java index 6c183467d3..6044e329c2 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java @@ -56,9 +56,9 @@ import org.sleuthkit.datamodel.TskCoreException; public final class CommonFilesPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; - + private final ComboBoxModel dataSourcesList; - private final Map dataSourceMap; + private final Map dataSourceMap; private static final Logger LOGGER = Logger.getLogger(CommonFilesPanel.class.getName()); private boolean singleDataSource; @@ -67,7 +67,7 @@ public final class CommonFilesPanel extends javax.swing.JPanel { /** * Creates new form CommonFilesPanel */ - @NbBundle.Messages({ + @NbBundle.Messages({ "CommonFilesPanel.title=Common Files Panel", "CommonFilesPanel.exception=Unexpected Exception loading DataSources."}) public CommonFilesPanel() { @@ -75,131 +75,51 @@ public final class CommonFilesPanel extends javax.swing.JPanel { String[] dataSourcesNames = new String[1]; this.dataSourceMap = new HashMap<>(); selectedDataSource = ""; - + try { buildDataSourceMap(dataSourceMap); dataSourcesNames = dataSourceMap.values().toArray(dataSourcesNames); } catch (TskCoreException | NoCurrentCaseException | SQLException ex) { - - LOGGER.log(Level.SEVERE, "Interrupted while loading Common Files Panel Data Sources", ex); - MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_exception()); + + LOGGER.log(Level.SEVERE, "Interrupted while loading Common Files Panel Data Sources", ex); + MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_exception()); } this.dataSourcesList = new DataSourceComboBoxModel(dataSourcesNames); initComponents(); updateUIButtons(); } - + private void updateUIButtons() { boolean multipleDataSources = this.caseHasMultipleSources(); allDataSourcesRadioButton.setEnabled(multipleDataSources); allDataSourcesRadioButton.setSelected(multipleDataSources); - if(!multipleDataSources) { - withinDataSourceRadioButton.setSelected(true); - withinDataSourceSelected(true); + if (!multipleDataSources) { + withinDataSourceRadioButton.setSelected(true); + withinDataSourceSelected(true); } } - - private boolean caseHasMultipleSources(){ + + private boolean caseHasMultipleSources() { return dataSourceMap.size() >= 2; } - - private void buildDataSourceMap(Map dataSourceMap) throws TskCoreException, NoCurrentCaseException, SQLException { - Case currentCase = Case.getOpenCase(); - SleuthkitCase tskDb = currentCase.getSleuthkitCase(); - CaseDbQuery query = tskDb.executeQuery("select obj_id, name from tsk_files where obj_id in (SELECT obj_id FROM tsk_objects WHERE obj_id in (select obj_id from data_source_info))"); - ResultSet resultSet = query.getResultSet(); - while(resultSet.next()){ - Long objectId = resultSet.getLong(1); - String dataSourceName = resultSet.getString(2); - dataSourceMap.put(objectId, dataSourceName); - } - + private void buildDataSourceMap(Map dataSourceMap) throws TskCoreException, NoCurrentCaseException, SQLException { + Case currentCase = Case.getOpenCase(); + SleuthkitCase tskDb = currentCase.getSleuthkitCase(); + CaseDbQuery query = tskDb.executeQuery("select obj_id, name from tsk_files where obj_id in (SELECT obj_id FROM tsk_objects WHERE obj_id in (select obj_id from data_source_info))"); + + ResultSet resultSet = query.getResultSet(); + while (resultSet.next()) { + Long objectId = resultSet.getLong(1); + String dataSourceName = resultSet.getString(2); + dataSourceMap.put(objectId, dataSourceName); + } + } - void addListenerToAll(ActionListener l) { //TODO double click the button this.searchButton.addActionListener(l); } - - private Map loadDataSourcesMap(SleuthkitCase sleuthkitCase) throws SQLException, TskCoreException { - Map dataSourceIdToNameMap = new HashMap<>(); - try ( - SleuthkitCase.CaseDbQuery query = sleuthkitCase.executeQuery("select obj_id, name from tsk_files where obj_id in (SELECT obj_id FROM tsk_objects WHERE obj_id in (select obj_id from data_source_info))"); - ResultSet resultSet = query.getResultSet()) { - - while (resultSet.next()) { - Long objectId = resultSet.getLong(1); - String dataSourceName = resultSet.getString(2); - dataSourceIdToNameMap.put(objectId, dataSourceName); - } - } - return dataSourceIdToNameMap; - } - private void addDataSource(Set dataSources, AbstractFile file, Map dataSourceIdToNameMap) { - long datasourceId = file.getDataSourceObjectId(); - String dataSourceName = dataSourceIdToNameMap.get(datasourceId); - dataSources.add(dataSourceName); - } - /** - * Sorts files in selection into a parent/child hierarchy where actual files - * are nested beneath a parent node which represents the common match. - * - * @return returns a reference to itself for ease of use. - * @throws TskCoreException - */ - private List collateFiles(Long selectedObjId) throws TskCoreException, SQLException { - - SleuthkitCase sleuthkitCase; - List metaDataModels = new ArrayList<>(); - Map> md5ToDataSourcesStringMap = new HashMap<>(); - - try { - sleuthkitCase = Case.getOpenCase().getSleuthkitCase(); - Map dataSourceIdToNameMap = loadDataSourcesMap(sleuthkitCase); - String whereClause = "md5 in (select md5 from tsk_files where (known != 1 OR known IS NULL) GROUP BY md5 HAVING COUNT(*) > 1) order by md5"; - if(selectedObjId != 0L) { - Object[] args = new String[] {Long.toString(selectedObjId), Long.toString(selectedObjId)}; - whereClause = String.format( - "md5 in (select md5 from tsk_files where data_source_obj_id=%s and (known != 1 OR known IS NULL) GROUP BY md5 HAVING COUNT(*) > 1) AND data_source_obj_id=%s order by md5", - args); - } - - List files = sleuthkitCase.findAllFilesWhere(whereClause); - - Map> parentNodes = new HashMap<>(); - - for (AbstractFile file : files) { - - String currentMd5 = file.getMd5Hash(); - - if (parentNodes.containsKey(currentMd5)) { - parentNodes.get(currentMd5).add(file); - Set currenDataSources = md5ToDataSourcesStringMap.get(currentMd5); - addDataSource(currenDataSources, file, dataSourceIdToNameMap); - md5ToDataSourcesStringMap.put(currentMd5, currenDataSources); - - } else { - List children = new ArrayList<>(); - Set dataSources = new HashSet<>(); - children.add(file); - parentNodes.put(currentMd5, children); - addDataSource(dataSources, file, dataSourceIdToNameMap); - md5ToDataSourcesStringMap.put(currentMd5, dataSources); - } - - } - for (String key : parentNodes.keySet()) { - metaDataModels.add(new CommonFilesMetaData(key, parentNodes.get(key), String.join(", ", md5ToDataSourcesStringMap.get(key)), dataSourceIdToNameMap)); - } - } catch (NoCurrentCaseException ex) { - Exceptions.printStackTrace(ex); - } - - - - return metaDataModels; - } @NbBundle.Messages({ "CommonFilesPanel.search.results.title=Common Files", @@ -216,22 +136,25 @@ public final class CommonFilesPanel extends javax.swing.JPanel { new SwingWorker, Void>() { + private Long determineDataSourceId() { + Long selectedObjId = 0L; + if (singleDataSource) { + for (Entry dataSource : dataSourceMap.entrySet()) { + if (dataSource.getValue().equals(selectedDataSource)) { + selectedObjId = dataSource.getKey(); + break; + } + } + } + return selectedObjId; + } + @Override @SuppressWarnings("FinallyDiscardsException") protected List doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException { + Long selectedObjId = determineDataSourceId(); + return new CommonFilesMetaDataBuilder(selectedObjId, dataSourceMap).collateFiles(); - //TODO cleanup - encapsulate business logic - Long selectedObjId = 0L; - if(singleDataSource) { - for (Entry dataSource : dataSourceMap.entrySet()) { - if (dataSource.getValue().equals(selectedDataSource)) { - selectedObjId = dataSource.getKey(); - break; - } - } - } - return collateFiles(selectedObjId); - } @Override @@ -248,14 +171,13 @@ public final class CommonFilesPanel extends javax.swing.JPanel { TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode); DataResultTopComponent component = DataResultTopComponent.createInstance(title); - + //component.enableTreeMode(); - int totalNodes = 0; - for(CommonFilesMetaData meta : metadata) { + for (CommonFilesMetaData meta : metadata) { totalNodes += meta.getChildren().size(); } - DataResultTopComponent.initInstance(pathText, tableFilterWithDescendantsNode, totalNodes, component); + DataResultTopComponent.initInstance(pathText, tableFilterWithDescendantsNode, totalNodes, component); } catch (InterruptedException ex) { LOGGER.log(Level.SEVERE, "Interrupted while loading Common Files", ex); @@ -281,16 +203,17 @@ public final class CommonFilesPanel extends javax.swing.JPanel { } }.execute(); } - + private class DataSourceComboBoxModel extends AbstractListModel implements ComboBoxModel { private static final long serialVersionUID = 1L; private final String[] dataSourceList; String selection = null; - + DataSourceComboBoxModel(String[] theDataSoureList) { dataSourceList = theDataSoureList; } + @Override public void setSelectedItem(Object anItem) { selection = (String) anItem; @@ -303,26 +226,25 @@ public final class CommonFilesPanel extends javax.swing.JPanel { @Override public int getSize() { - return dataSourceList.length; + return dataSourceList.length; } @Override public String getElementAt(int index) { - return dataSourceList[index]; + return dataSourceList[index]; } @Override public void addListDataListener(ListDataListener l) { - this.listenerList.add(ListDataListener.class, l); + this.listenerList.add(ListDataListener.class, l); } @Override public void removeListDataListener(ListDataListener l) { - this.listenerList.remove(ListDataListener.class, l); + this.listenerList.remove(ListDataListener.class, l); } - - + } /** @@ -409,9 +331,9 @@ public final class CommonFilesPanel extends javax.swing.JPanel { }//GEN-LAST:event_searchButtonActionPerformed private void allDataSourcesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allDataSourcesRadioButtonActionPerformed - selectDataSourceComboBox.setEnabled(!allDataSourcesRadioButton.isSelected()); - singleDataSource = false; - + selectDataSourceComboBox.setEnabled(!allDataSourcesRadioButton.isSelected()); + singleDataSource = false; + }//GEN-LAST:event_allDataSourcesRadioButtonActionPerformed private void selectDataSourceComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectDataSourceComboBoxActionPerformed @@ -424,7 +346,7 @@ public final class CommonFilesPanel extends javax.swing.JPanel { private void withinDataSourceSelected(boolean selected) { selectDataSourceComboBox.setEnabled(selected); - if(selectDataSourceComboBox.isEnabled()) { + if (selectDataSourceComboBox.isEnabled()) { selectDataSourceComboBox.setSelectedIndex(0); singleDataSource = true; } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchNode.java index c2cb71e219..45dfb194e8 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchNode.java @@ -19,20 +19,23 @@ package org.sleuthkit.autopsy.commonfilesearch; import java.util.List; +import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; +import org.openide.nodes.Node; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.datamodel.CommonFileParentNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; /** - * Encapsulates data used to display common files search results in the top - * right pane. + * Wrapper node for CommonFilesParentNode used to display common files search results in the top + * right pane. Calls CommonFilesParentFactory. */ final public class CommonFilesSearchNode extends DisplayableItemNode { CommonFilesSearchNode(List metaDataList) { - super(Children.create(new CommonFilesChildren(metaDataList), true), Lookups.singleton(metaDataList)); + super(Children.create(new CommonFilesParentFactory(metaDataList), true), Lookups.singleton(metaDataList)); } @NbBundle.Messages({ @@ -56,4 +59,39 @@ final public class CommonFilesSearchNode extends DisplayableItemNode { public String getItemType() { return getClass().getName(); } + +/** + * ChildFactory which builds CommonFileParentNodes from the CommonFilesMetaaData models. + */ +static class CommonFilesParentFactory extends ChildFactory { + + /** + * List of models, each of which is a parent node matching a single md5, containing children FileNodes. + */ + private List metaDataList; + + CommonFilesParentFactory(List theMetaDataList) { + this.metaDataList = theMetaDataList; + } + + protected void removeNotify() { + metaDataList = null; + } + + @Override + protected Node createNodeForKey(CommonFilesMetaData metaData) { + + return new CommonFileParentNode(metaData); + } + + @Override + protected boolean createKeys(List toPopulate) { + toPopulate.addAll(metaDataList); + return true; + } + + } + +} + diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/CommonFileParentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/CommonFileParentNode.java index 7bbbf1160d..16eebd438e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/CommonFileParentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/CommonFileParentNode.java @@ -20,13 +20,16 @@ package org.sleuthkit.autopsy.datamodel; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +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.CommonFilesDescendants; import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetaData; +import org.sleuthkit.datamodel.AbstractFile; /** * Represents a common files match - two or more files which appear to be the @@ -40,7 +43,7 @@ public class CommonFileParentNode extends DisplayableItemNode { public CommonFileParentNode(CommonFilesMetaData metaData) { super(Children.create( - new CommonFilesDescendants(metaData.getChildren(), + new CommonFilesChildFactory(metaData.getChildren(), metaData.getDataSourceIdToNameMap()), true), Lookups.singleton(metaData)); this.commonFileCount = metaData.getChildren().size(); @@ -110,7 +113,36 @@ public class CommonFileParentNode extends DisplayableItemNode { public String getItemType() { return getClass().getName(); } + + /** + * Child generator for FileNodes of CommonFileParentNodes + */ + static class CommonFilesChildFactory extends ChildFactory { + + private final List descendants; + private final Map dataSourceMap; + + CommonFilesChildFactory(List descendants, Map dataSourceMap){ + this.descendants = descendants; + this.dataSourceMap = dataSourceMap; + } + + @Override + protected Node createNodeForKey(AbstractFile file){ + + final String dataSource = this.dataSourceMap.get(file.getDataSourceObjectId()); + + return new CommonFileChildNode(file, dataSource); + } + + @Override + protected boolean createKeys(List list) { + list.addAll(this.descendants); + return true; + } + + } @NbBundle.Messages({ "CommonFileParentPropertyType.fileColLbl=File", "CommonFileParentPropertyType.instanceColLbl=Instance Count",