diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HostPersonDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HostPersonDAO.java index fa48b7db88..b7168a5da4 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HostPersonDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HostPersonDAO.java @@ -30,6 +30,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.mainui.datamodel.TreeResultsDTO.TreeDisplayCount; import org.sleuthkit.autopsy.mainui.datamodel.TreeResultsDTO.TreeItemDTO; import org.sleuthkit.autopsy.mainui.datamodel.events.DAOEvent; +import org.sleuthkit.autopsy.mainui.datamodel.events.HostPersonEvent; import org.sleuthkit.autopsy.mainui.datamodel.events.TreeEvent; import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.Person; @@ -106,31 +107,26 @@ public class HostPersonDAO extends AbstractDAO { person.getName(), TreeDisplayCount.NOT_SHOWN); } + + private static final Set caseEvents = Stream.of( + Case.Events.PERSONS_ADDED, + Case.Events.PERSONS_DELETED, + Case.Events.PERSONS_UPDATED, + Case.Events.HOSTS_ADDED, + Case.Events.HOSTS_ADDED_TO_PERSON, + Case.Events.HOSTS_DELETED, + Case.Events.HOSTS_REMOVED_FROM_PERSON, + Case.Events.HOSTS_UPDATED + ) + .map(caseEvent -> caseEvent.toString()) + .collect(Collectors.toSet()); + @Override Set processEvent(PropertyChangeEvent evt) { - // GVDTODO - String eventName = evt.getPropertyName(); - if (Case.Events.PERSONS_ADDED.toString().equals(eventName)) { - - } else if (Case.Events.PERSONS_DELETED.toString().equals(eventName)) { - - } else if (Case.Events.PERSONS_UPDATED.toString().equals(eventName)) { - - } else if (Case.Events.HOSTS_ADDED.toString().equals(eventName)) { - - } else if (Case.Events.HOSTS_ADDED_TO_PERSON.toString().equals(eventName)) { - - } else if (Case.Events.HOSTS_DELETED.toString().equals(eventName)) { - - } else if (Case.Events.HOSTS_REMOVED_FROM_PERSON.toString().equals(eventName)) { - - } else if (Case.Events.HOSTS_UPDATED.toString().equals(eventName)) { - - } -// if (CaseEvent.DATA_ADDED.toString().equals(eventName) -// && (evt.getOldValue() instanceof ModuleDataEvent)) { -// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + return caseEvents.contains(evt.getPropertyName()) + ? Collections.singleton(new HostPersonEvent()) + : Collections.emptySet(); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/events/HostPersonEvent.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/events/HostPersonEvent.java new file mode 100644 index 0000000000..afa011c138 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/events/HostPersonEvent.java @@ -0,0 +1,30 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.mainui.datamodel.events; + +/** + * An event that hosts or persons were changed. + */ +public class HostPersonEvent implements DAOEvent { + + @Override + public Type getType() { + return Type.TREE; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/mainui/nodes/RootFactory.java b/Core/src/org/sleuthkit/autopsy/mainui/nodes/RootFactory.java index bd7e660509..ba28977819 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/nodes/RootFactory.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/nodes/RootFactory.java @@ -18,68 +18,108 @@ */ package org.sleuthkit.autopsy.mainui.nodes; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.Arrays; import java.util.Comparator; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; +import org.openide.nodes.Node; import org.openide.util.NbBundle.Messages; +import org.openide.util.WeakListeners; import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.mainui.datamodel.HostSearchParams; import org.sleuthkit.autopsy.mainui.datamodel.MainDAO; import org.sleuthkit.autopsy.mainui.datamodel.OsAccountsSearchParams; import org.sleuthkit.autopsy.mainui.datamodel.PersonSearchParams; import org.sleuthkit.autopsy.mainui.datamodel.ReportsSearchParams; import org.sleuthkit.autopsy.mainui.datamodel.TreeResultsDTO; +import org.sleuthkit.autopsy.mainui.datamodel.TreeResultsDTO.TreeItemDTO; +import org.sleuthkit.autopsy.mainui.datamodel.events.DAOAggregateEvent; +import org.sleuthkit.autopsy.mainui.datamodel.events.DAOEvent; +import org.sleuthkit.autopsy.mainui.datamodel.events.HostPersonEvent; import org.sleuthkit.autopsy.mainui.datamodel.events.TreeEvent; import org.sleuthkit.autopsy.mainui.nodes.TreeNode.StaticTreeNode; +import static org.sleuthkit.autopsy.mainui.nodes.TreeNode.getDefaultLookup; import org.sleuthkit.datamodel.Person; /** * * Root tree view factories. */ +@Messages({"RootFactory_unknownPersons_displayName=Unknown Persons"}) public class RootFactory { public Children getRootChildren() { + // GVDTODO integrate if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { return Children.create(new HostPersonRootFactory(), true); } else { return new DefaultViewRootFactory(); } } - + private static String getLongString(Long l) { return l == null ? "" : l.toString(); } - public static class HostPersonRootFactory extends TreeChildFactory { + /** + * Factory for populating child nodes in a tree based on TreeResultsDTO + */ + static class HostPersonRootFactory extends ChildFactory.Detachable> { + + private static final Logger logger = Logger.getLogger(HostPersonRootFactory.class.getName()); + + private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { + if (evt.getNewValue() instanceof DAOAggregateEvent) { + for (DAOEvent daoEvt : ((DAOAggregateEvent) evt.getNewValue()).getEvents()) { + if (daoEvt instanceof HostPersonEvent) { + HostPersonRootFactory.this.refresh(false); + return; + } + } + } + }; + + private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, MainDAO.getInstance().getTreeEventsManager()); @Override - protected TreeNode createNewNode(TreeResultsDTO.TreeItemDTO rowData) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + protected boolean createKeys(List> toPopulate) { + try { + TreeResultsDTO persons = MainDAO.getInstance().getHostPersonDAO().getAllPersons(); + if (persons.getItems().isEmpty() || (persons.getItems().size() == 1 && persons.getItems().get(0).getSearchParams().getPerson() == null)) { + toPopulate.addAll(MainDAO.getInstance().getHostPersonDAO().getAllHosts().getItems()); + } else { + toPopulate.addAll(persons.getItems()); + } + } catch (ExecutionException | IllegalArgumentException ex) { + logger.log(Level.WARNING, "Error acquiring top-level host/person data", ex); + } + + return true; } @Override - protected TreeResultsDTO getChildResults() throws IllegalArgumentException, ExecutionException { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + protected Node createNodeForKey(TreeItemDTO key) { + if (key.getSearchParams() instanceof HostSearchParams) { + return new HostNode((TreeItemDTO) key); + } else if (key.getSearchParams() instanceof PersonSearchParams) { + return new PersonNode((TreeItemDTO) key); + } else { + return null; + } } - - @Override - protected TreeResultsDTO.TreeItemDTO getOrCreateRelevantChild(TreeEvent treeEvt) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public int compare(TreeResultsDTO.TreeItemDTO o1, TreeResultsDTO.TreeItemDTO o2) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - } - + public static class DefaultViewRootFactory extends Children.Array { + public DefaultViewRootFactory() { super(Arrays.asList( new AllDataSourcesNode(), @@ -92,15 +132,27 @@ public class RootFactory { )); } } - + @Messages({"RootFactory_AllDataSourcesNode_displayName=Data Sources"}) public static class AllDataSourcesNode extends StaticTreeNode { + public AllDataSourcesNode() { super("ALL_DATA_SOURCES", Bundle.RootFactory_AllDataSourcesNode_displayName(), "org/sleuthkit/autopsy/images/image.png", new HostFactory(Optional.empty())); - } + } + } + + public static class PersonNode extends TreeNode { + + public PersonNode(TreeResultsDTO.TreeItemDTO itemData) { + super(PersonSearchParams.getTypeId(), + "org/sleuthkit/autopsy/images/person.png", + itemData, + Children.create(new HostFactory(Optional.of(itemData.getSearchParams().getPerson())), true), + getDefaultLookup(itemData)); + } } public static class HostFactory extends TreeChildFactory { @@ -113,7 +165,7 @@ public class RootFactory { @Override protected TreeNode createNewNode(TreeResultsDTO.TreeItemDTO rowData) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + return new HostNode(rowData); } @Override @@ -125,24 +177,40 @@ public class RootFactory { } } + @Override + protected void handleDAOAggregateEvent(DAOAggregateEvent aggEvt) { + for (DAOEvent evt : aggEvt.getEvents()) { + if (evt instanceof HostPersonEvent) { + super.update(); + return; + } + } + } + @Override protected TreeResultsDTO.TreeItemDTO getOrCreateRelevantChild(TreeEvent treeEvt) { - // GVDTODO - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + return null; } @Override public int compare(TreeResultsDTO.TreeItemDTO o1, TreeResultsDTO.TreeItemDTO o2) { return Comparator.comparing((TreeResultsDTO.TreeItemDTO h) -> h.getSearchParams().getHost().getName()).compare(o1, o2); } - - - - + } + public static class HostNode extends TreeNode { + + public HostNode(TreeResultsDTO.TreeItemDTO itemData) { + super(HostSearchParams.getTypeId(), + "org/sleuthkit/autopsy/images/host.png", + itemData, + Children.create(new FileSystemFactory(itemData.getSearchParams().getHost()), true), + getDefaultLookup(itemData)); + } } public static class DataSourceGroupedNode extends StaticTreeNode { + public DataSourceGroupedNode(long dataSourceObjId, String dsName, boolean isImage) { super("DATA_SOURCE_GROUPED_" + dataSourceObjId, dsName, @@ -199,7 +267,7 @@ public class RootFactory { new DataArtifactTypeFactory(dataSourceObjId)); } } - + @Messages({"RootFactory_AnalysisResultsRootNode_displayName=Analysis Results"}) public static class AnalysisResultsRootNode extends StaticTreeNode { @@ -220,7 +288,7 @@ public class RootFactory { super("DATA_SOURCE_BY_TYPE_" + getLongString(dataSourceObjId), Bundle.RootFactory_OsAccountsRootNode_displayName(), "org/sleuthkit/autopsy/images/os-account.png"); - + this.dataSourceObjId = dataSourceObjId; } @@ -228,12 +296,12 @@ public class RootFactory { public void respondSelection(DataResultTopComponent dataResultPanel) { dataResultPanel.displayOsAccounts(new OsAccountsSearchParams(dataSourceObjId)); } - - + } @Messages({"RootFactory_TagsRootNode_displayName=Tags"}) public static class TagsRootNode extends StaticTreeNode { + public TagsRootNode(Long dataSourceObjId) { super("DATA_SOURCE_BY_TYPE_" + getLongString(dataSourceObjId), Bundle.RootFactory_TagsRootNode_displayName(),