From cb8eb44104af8bedf85187f445c91d917283261b Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 15 Feb 2018 13:16:37 +0100 Subject: [PATCH 1/3] factor lockedVertexModel and PinnedAccountModel out of CommunicationsGraph --- .../communications/CommunicationsGraph.java | 138 ++++++++---------- .../communications/LockedVertexModel.java | 86 +++++++++++ .../communications/PinnedAccountModel.java | 71 +++++++++ .../communications/VisualizationPanel.java | 32 ++-- .../visualization/EventHandler.java | 9 ++ 5 files changed, 243 insertions(+), 93 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index b7559141ec..dd0adef1d4 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -20,9 +20,9 @@ package org.sleuthkit.autopsy.communications; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; +import com.google.common.eventbus.Subscribe; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; import com.mxgraph.util.mxConstants; @@ -44,6 +44,7 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.SwingWorker; +import org.sleuthkit.autopsy.communications.visualization.EventHandler; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.AccountDeviceInstance; @@ -52,6 +53,10 @@ import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; +/** + * Implementation of mxGraph customized for our use in the CVT visualize mode. + * Acts as the primary entry point into the JGraphX API. + */ final class CommunicationsGraph extends mxGraph { private static final Logger logger = Logger.getLogger(CommunicationsGraph.class.getName()); @@ -68,12 +73,11 @@ final class CommunicationsGraph extends mxGraph { labelMustache = new DefaultMustacheFactory().compile(new InputStreamReader(templateStream), "Vertex_Label"); } + /** + * Style sheet for default vertex and edge styles. These are initialized in + * the static block below. + */ static final private mxStylesheet mxStylesheet = new mxStylesheet(); - private final Set pinnedAccountDevices = new HashSet<>(); - private final Set lockedVertices = new HashSet<>(); - - private final Map nodeMap = new HashMap<>(); - private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); static { //initialize defaul vertex properties @@ -88,8 +92,22 @@ final class CommunicationsGraph extends mxGraph { mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE); } + /** + * Map from type specific account identifier to mxCell(vertex). + */ + private final Map nodeMap = new HashMap<>(); + + /** + * Map from relationship source (Content) to mxCell (edge). + */ + private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); + private final LockedVertexModel lockedVertexModel; + + private final PinnedAccountModel pinnedAccountModel; + CommunicationsGraph() { super(mxStylesheet); + //set fixed properties of graph. setAutoSizeCells(true); setCellsCloneable(false); setDropEnabled(false); @@ -107,6 +125,33 @@ final class CommunicationsGraph extends mxGraph { setKeepEdgesInBackground(true); setResetEdgesOnMove(true); setHtmlLabels(true); + + lockedVertexModel = new LockedVertexModel(); + lockedVertexModel.registerhandler(new EventHandler() { + @Override + @Subscribe + public void handle(LockedVertexModel.VertexLockEvent event) { + if (event.isVertexLocked()) { + getView().clear(event.getVertex(), true, true); + getView().validate(); + } else { + final mxCellState state = getView().getState(event.getVertex(), true); + getView().updateLabel(state); + getView().updateLabelBounds(state); + getView().updateBoundingBox(state); + } + } + }); + + pinnedAccountModel = new PinnedAccountModel(this); + } + + public LockedVertexModel getLockedVertexModel() { + return lockedVertexModel; + } + + public PinnedAccountModel getPinnedAccountModel() { + return pinnedAccountModel; } void clear() { @@ -115,10 +160,6 @@ final class CommunicationsGraph extends mxGraph { removeCells(getChildVertices(getDefaultParent())); } - boolean isAccountPinned(AccountDeviceInstanceKey account) { - return pinnedAccountDevices.contains(account); - } - @Override public String convertValueToString(Object cell) { final StringWriter stringWriter = new StringWriter(); @@ -132,9 +173,9 @@ final class CommunicationsGraph extends mxGraph { scopes.put("size", Math.round(Math.log(adiKey.getMessageCount()) + 5)); scopes.put("iconFileName", CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); - scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); + scopes.put("pinned", pinnedAccountModel.isAccountPinned(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); - scopes.put("locked", lockedVertices.contains((mxCell) cell)); + scopes.put("locked", lockedVertexModel.isVertexLocked((mxCell) cell)); scopes.put("LOCK_URL", LOCK_URL); labelMustache.execute(stringWriter, scopes); @@ -158,9 +199,9 @@ final class CommunicationsGraph extends mxGraph { scopes.put("size", 12);// Math.round(Math.log(adiKey.getMessageCount()) + 5)); scopes.put("iconFileName", CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); - scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); + scopes.put("pinned", pinnedAccountModel.isAccountPinned(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); - scopes.put("locked", lockedVertices.contains((mxCell) cell)); + scopes.put("locked", lockedVertexModel.isVertexLocked((mxCell) cell)); scopes.put("LOCK_URL", LOCK_URL); labelMustache.execute(stringWriter, scopes); @@ -171,54 +212,6 @@ final class CommunicationsGraph extends mxGraph { } } - /** - * Unpin the given accounts from the graph. Pinned accounts will always be - * shown regardless of the filter state. Furthermore, accounts with - * relationships that pass the filters will also be shown. - * - * @param accountDeviceInstances The accounts to unpin. - */ - void unpinAccount(ImmutableSet accountDeviceInstances) { - pinnedAccountDevices.removeAll(accountDeviceInstances); - } - - /** - * Pin the given accounts to the graph. Pinned accounts will always be shown - * regardless of the filter state. Furthermore, accounts with relationships - * that pass the filters will also be shown. - * - * @param accountDeviceInstances The accounts to pin. - */ - void pinAccount(ImmutableSet accountDeviceInstances) { - pinnedAccountDevices.addAll(accountDeviceInstances); - } - - /** - * Lock the given vertex so that applying a layout algorithm doesn't move - * it. The user can still manually position the vertex. - * - * @param vertex The vertex to lock. - */ - void lockVertex(mxCell vertex) { - lockedVertices.add(vertex); - getView().clear(vertex, true, true); - getView().validate(); - } - - /** - * Lock the given vertex so that applying a layout algorithm can move it. - * - * @param vertex The vertex to unlock. - */ - void unlockVertex(mxCell vertex) { - lockedVertices.remove(vertex); - - final mxCellState state = getView().getState(vertex, true); - getView().updateLabel(state); - getView().updateLabelBounds(state); - getView().updateBoundingBox(state); - } - SwingWorker rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { return new RebuildWorker(progress, commsManager, currentFilter); } @@ -226,8 +219,8 @@ final class CommunicationsGraph extends mxGraph { void resetGraph() { clear(); getView().setScale(1); - pinnedAccountDevices.clear(); - lockedVertices.clear(); + pinnedAccountModel.clear(); + lockedVertexModel.clear(); } private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { @@ -275,21 +268,6 @@ final class CommunicationsGraph extends mxGraph { return edge; } - /** - * Are there any accounts in this graph? If there are no pinned accounts the - * graph will be empty. - * - * @return True if this graph is empty. - */ - boolean isEmpty() { - return pinnedAccountDevices.isEmpty(); - } - - boolean isVertexLocked(mxCell vertex) { - return lockedVertices.contains(vertex); - - } - /** * SwingWorker that loads the accounts and edges for this graph according to * the pinned accounts and the current filters. @@ -317,7 +295,7 @@ final class CommunicationsGraph extends mxGraph { * set to keep track of accounts related to pinned accounts */ Set relatedAccounts = new HashSet<>(); - for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { + for (AccountDeviceInstanceKey adiKey : pinnedAccountModel.getPinnedAccounts()) { if (isCancelled()) { break; } diff --git a/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java b/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java new file mode 100644 index 0000000000..fd84f6d31b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java @@ -0,0 +1,86 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.communications; + +import com.google.common.eventbus.EventBus; +import com.mxgraph.model.mxCell; +import java.util.HashSet; +import java.util.Set; +import org.sleuthkit.autopsy.communications.visualization.EventHandler; + +class LockedVertexModel { + + void registerhandler(EventHandler handler) { + eventBus.register(handler); + } + + void unregisterhandler(EventHandler handler) { + eventBus.unregister(handler); + } + + private final EventBus eventBus = new EventBus(); + + /** + * Set of mxCells (vertices) that are 'locked'. Locked vertices are not + * affected by layout algorithms, but may be repositioned manually by the + * user. + */ + private final Set lockedVertices = new HashSet<>(); + + LockedVertexModel() { + } + + /** + * Lock the given vertex so that applying a layout algorithm doesn't move + * it. The user can still manually position the vertex. + * + * @param vertex The vertex to lock. + */ + void lockVertex(mxCell vertex) { + lockedVertices.add(vertex); + eventBus.post(new VertexLockEvent(vertex, true)); + + } + + /** + * Lock the given vertex so that applying a layout algorithm can move it. + * + * @param vertex The vertex to unlock. + */ + void unlockVertex(mxCell vertex) { + lockedVertices.remove(vertex); + eventBus.post(new VertexLockEvent(vertex, false)); + + } + + boolean isVertexLocked(mxCell vertex) { + return lockedVertices.contains(vertex); + + } + + void clear() { + lockedVertices.clear(); + } + + static class VertexLockEvent { + + private final mxCell vertex; + + public mxCell getVertex() { + return vertex; + } + + public boolean isVertexLocked() { + return locked; + } + private final boolean locked; + + VertexLockEvent(mxCell vertex, boolean locked) { + this.vertex = vertex; + this.locked = locked; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java b/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java new file mode 100644 index 0000000000..2afa5b8318 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java @@ -0,0 +1,71 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.communications; + +import com.google.common.collect.ImmutableSet; +import java.util.HashSet; +import java.util.Set; + +class PinnedAccountModel { + + /** + * Set of AccountDeviceInstanceKeys that are 'Pinned' to this graph. Pinned + * accounts are shown regardless of filters, and accounts that are related + * to pinned accounts and pass the filters are show. Pinning accounts is the + * primary way to populate the graph. + */ + private final Set pinnedAccountDevices = new HashSet<>(); + private final CommunicationsGraph graph; + + PinnedAccountModel(CommunicationsGraph graph) { + this.graph = graph; + } + + boolean isAccountPinned(AccountDeviceInstanceKey account) { + return pinnedAccountDevices.contains(account); + } + + /** + * Unpin the given accounts from the graph. Pinned accounts will always be + * shown regardless of the filter state. Furthermore, accounts with + * relationships that pass the filters will also be shown. + * + * @param accountDeviceInstances The accounts to unpin. + */ + void unpinAccount(ImmutableSet accountDeviceInstances) { + pinnedAccountDevices.removeAll(accountDeviceInstances); + } + + /** + * Pin the given accounts to the graph. Pinned accounts will always be shown + * regardless of the filter state. Furthermore, accounts with relationships + * that pass the filters will also be shown. + * + * @param accountDeviceInstances The accounts to pin. + */ + void pinAccount(ImmutableSet accountDeviceInstances) { + pinnedAccountDevices.addAll(accountDeviceInstances); + } + + /** + * Are there any accounts in this graph? If there are no pinned accounts the + * graph will be empty. + * + * @return True if this graph is empty. + */ + boolean isEmpty() { + return pinnedAccountDevices.isEmpty(); + } + + void clear() { + pinnedAccountDevices.clear(); + } + + Iterable getPinnedAccounts() { + return ImmutableSet.copyOf(pinnedAccountDevices); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 0d0991f840..12b66824e6 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -91,8 +91,9 @@ import org.sleuthkit.datamodel.TskCoreException; /** * A panel that goes in the Visualize tab of the Communications Visualization - * Tool. Hosts an JGraphX mxGraphComponent that host the communications network - * visualization and a MessageBrowser for viewing details of communications. + * Tool. Hosts an JGraphX mxGraphComponent that implements the communications + * network visualization and a MessageBrowser for viewing details of + * communications. * * The Lookup provided by getLookup will be proxied by the lookup of the * CVTTopComponent when this tab is active allowing for context sensitive @@ -140,10 +141,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private SwingWorker worker; + private final PinnedAccountModel pinnedAccountModel; + private final LockedVertexModel lockedVertexModel; public VisualizationPanel() { initComponents(); graph = new CommunicationsGraph(); + pinnedAccountModel = graph.getPinnedAccountModel(); + lockedVertexModel = graph.getLockedVertexModel(); fastOrganicLayout = new mxFastOrganicLayoutImpl(graph); circleLayout = new mxCircleLayoutImpl(graph); @@ -193,22 +198,22 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final JPopupMenu jPopupMenu = new JPopupMenu(); final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue(); - if (graph.isVertexLocked(cellAt)) { + if (lockedVertexModel.isVertexLocked(cellAt)) { jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock " + cellAt.getId(), unlockIcon) { @Override public void actionPerformed(ActionEvent e) { - graph.unlockVertex(cellAt); + lockedVertexModel.unlockVertex(cellAt); } })); } else { jPopupMenu.add(new JMenuItem(new AbstractAction("Lock " + cellAt.getId(), lockIcon) { @Override public void actionPerformed(ActionEvent e) { - graph.lockVertex(cellAt); + lockedVertexModel.lockVertex(cellAt); } })); } - if (graph.isAccountPinned(adiKey)) { + if (pinnedAccountModel.isAccountPinned(adiKey)) { jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin " + cellAt.getId(), unpinIcon) { @Override public void actionPerformed(ActionEvent e) { @@ -247,6 +252,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Override + public Lookup getLookup() { return proxyLookup; } @@ -254,7 +260,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Subscribe void handleUnPinEvent(CVTEvents.UnpinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); - graph.unpinAccount(pinEvent.getAccountDeviceInstances()); + pinnedAccountModel.unpinAccount(pinEvent.getAccountDeviceInstances()); graph.clear(); rebuildGraph(); // Updates the display @@ -268,7 +274,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider if (pinEvent.isReplace()) { graph.resetGraph(); } - graph.pinAccount(pinEvent.getAccountDeviceInstances()); + pinnedAccountModel.pinAccount(pinEvent.getAccountDeviceInstances()); rebuildGraph(); // Updates the display graph.getModel().endUpdate(); @@ -289,7 +295,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private void rebuildGraph() { - if (graph.isEmpty()) { + if (pinnedAccountModel.isEmpty()) { borderLayoutPanel.remove(graphComponent); borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); repaint(); @@ -743,7 +749,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public boolean isVertexIgnored(Object vertex) { return super.isVertexIgnored(vertex) - || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + || lockedVertexModel.isVertexLocked((mxCell) vertex); } @Override @@ -766,7 +772,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public boolean isVertexIgnored(Object vertex) { return super.isVertexIgnored(vertex) - || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + || lockedVertexModel.isVertexLocked((mxCell) vertex); } @Override @@ -789,7 +795,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public boolean isVertexIgnored(Object vertex) { return super.isVertexIgnored(vertex) - || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + || lockedVertexModel.isVertexLocked((mxCell) vertex); } @Override @@ -811,7 +817,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public boolean isVertexIgnored(Object vertex) { return super.isVertexIgnored(vertex) - || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + || lockedVertexModel.isVertexLocked((mxCell) vertex); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java b/Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java new file mode 100644 index 0000000000..539da4be4a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java @@ -0,0 +1,9 @@ +package org.sleuthkit.autopsy.communications.visualization; + +/** + * + */ +public interface EventHandler { + + void handle(T event); +} From 81b7b471aba3b3d330ae71a47df58c445a6b47de Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 16 Feb 2018 16:02:28 +0100 Subject: [PATCH 2/3] make sure pinned accounts always show in visualization; cleanup --- .../autopsy/communications/Bundle.properties | 23 ++++++++------- .../communications/CVTTopComponent.form | 2 +- .../communications/CommunicationsGraph.java | 7 +++-- .../autopsy/communications/EventHandler.java | 28 +++++++++++++++++++ .../communications/LockedVertexModel.java | 20 ++++++++++--- .../communications/PinnedAccountModel.java | 19 +++++++++++-- .../visualization/EventHandler.java | 9 ------ 7 files changed, 78 insertions(+), 30 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/EventHandler.java delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index dfbdb97ed0..96d76fe34a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -24,19 +24,22 @@ VisualizationPanel.jButton6.text=Hierarchy VisualizationPanel.jButton7.text=Circle VisualizationPanel.jButton8.text=Organic VisualizationPanel.fitGraphButton.text= -VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. -VisualizationPanel.jLabel1.text=Layouts: -VisualizationPanel.fitZoomButton.text= -VisualizationPanel.zoomActualButton.text= -VisualizationPanel.zoomInButton.text= VisualizationPanel.zoomOutButton.text= +# To change this license header, choose License Headers in Project Properties. +# To change this template file, choose Tools | Templates +# and open the template in the editor. VisualizationPanel.circleLayoutButton.text=Circle +VisualizationPanel.organicLayoutButton.text=Organic VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic VisualizationPanel.hierarchyLayoutButton.text=Hierarchy -VisualizationPanel.jLabel2.text=Zoom: VisualizationPanel.zoomLabel.text=100% -VisualizationPanel.zoomOutButton.toolTipText=Zoom out -VisualizationPanel.zoomInButton.toolTipText=Zoom in -VisualizationPanel.zoomActualButton.toolTipText=reset zoom +VisualizationPanel.jLabel1.text=Layouts: +VisualizationPanel.jLabel2.text=Zoom: VisualizationPanel.fitZoomButton.toolTipText=fit visualization -VisualizationPanel.organicLayoutButton.text=Organic +VisualizationPanel.fitZoomButton.text= +VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. +VisualizationPanel.zoomActualButton.toolTipText=reset zoom +VisualizationPanel.zoomActualButton.text= +VisualizationPanel.zoomInButton.toolTipText=Zoom in +VisualizationPanel.zoomInButton.text= +VisualizationPanel.zoomOutButton.toolTipText=Zoom out diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form index 9315465965..75898c859e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form @@ -92,4 +92,4 @@ - + \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index dd0adef1d4..f278bc311b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -44,7 +44,6 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.SwingWorker; -import org.sleuthkit.autopsy.communications.visualization.EventHandler; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.AccountDeviceInstance; @@ -146,11 +145,11 @@ final class CommunicationsGraph extends mxGraph { pinnedAccountModel = new PinnedAccountModel(this); } - public LockedVertexModel getLockedVertexModel() { + LockedVertexModel getLockedVertexModel() { return lockedVertexModel; } - public PinnedAccountModel getPinnedAccountModel() { + PinnedAccountModel getPinnedAccountModel() { return pinnedAccountModel; } @@ -302,6 +301,8 @@ final class CommunicationsGraph extends mxGraph { List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); relatedAccounts.add(adiKey); + getOrCreateVertex(adiKey); + //get accounts related to pinned account for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { // handle.progress(1); diff --git a/Core/src/org/sleuthkit/autopsy/communications/EventHandler.java b/Core/src/org/sleuthkit/autopsy/communications/EventHandler.java new file mode 100644 index 0000000000..f1acedf1cf --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/EventHandler.java @@ -0,0 +1,28 @@ +/* + * 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.communications; + +/** + * + */ +public interface EventHandler { + + void handle(T event); +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java b/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java index fd84f6d31b..f26e9515b3 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * 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.communications; @@ -9,7 +22,6 @@ import com.google.common.eventbus.EventBus; import com.mxgraph.model.mxCell; import java.util.HashSet; import java.util.Set; -import org.sleuthkit.autopsy.communications.visualization.EventHandler; class LockedVertexModel { diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java b/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java index 2afa5b8318..d5f7a7cc03 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * 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.communications; diff --git a/Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java b/Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java deleted file mode 100644 index 539da4be4a..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.sleuthkit.autopsy.communications.visualization; - -/** - * - */ -public interface EventHandler { - - void handle(T event); -} From 85d9577ea2cb46d5fefda58956b7660ccb217e1a Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 21 Feb 2018 13:21:02 +0100 Subject: [PATCH 3/3] use new getRelationshionshipsCount(a1,a2) method to improve graph loading time --- .../communications/CommunicationsGraph.java | 91 +++++++++++-------- .../communications/VisualizationPanel.java | 16 +++- 2 files changed, 65 insertions(+), 42 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index f278bc311b..81ec5d7838 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -33,10 +33,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; import java.net.URL; -import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -50,6 +47,7 @@ import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.Relationship; import org.sleuthkit.datamodel.TskCoreException; /** @@ -239,30 +237,29 @@ final class CommunicationsGraph extends mxGraph { size); return newVertex; }); - final mxCellState state = getView().getState(vertex, true); - - getView().updateLabel(state); - getView().updateLabelBounds(state); - getView().updateBoundingBox(state); +// final mxCellState state = getView().getState(vertex, true); +// +// getView().updateLabel(state); +// getView().updateLabelBounds(state); +// getView().updateBoundingBox(state); return vertex; } @SuppressWarnings("unchecked") - private mxCell addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) { + private mxCell addOrUpdateEdge(long relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) { mxCell vertex1 = getOrCreateVertex(account1); mxCell vertex2 = getOrCreateVertex(account2); Object[] edgesBetween = getEdgesBetween(vertex1, vertex2); mxCell edge; if (edgesBetween.length == 0) { final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); - final HashSet hashSet = new HashSet<>(relSources); - edge = (mxCell) insertEdge(getDefaultParent(), edgeName, hashSet, vertex1, vertex2, - "strokeWidth=" + (Math.log(hashSet.size()) + 1)); + edge = (mxCell) insertEdge(getDefaultParent(), edgeName, relSources, vertex1, vertex2, + "strokeWidth=" + (Math.log(relSources) + 1)); } else { edge = (mxCell) edgesBetween[0]; - ((Collection) edge.getValue()).addAll(relSources); - edge.setStyle("strokeWidth=" + (Math.log(((Collection) edge.getValue()).size()) + 1)); +// ((Collection) edge.getValue()).addAll(relSources); + edge.setStyle("strokeWidth=" + (Math.log(relSources) + 1)); } return edge; } @@ -287,54 +284,68 @@ final class CommunicationsGraph extends mxGraph { @Override protected Void doInBackground() throws Exception { progress.start("Loading accounts"); -// progress.switchToDeterminate("Loading accounts", 0,pinnedAccountDevices.size()); int i = 0; try { /** * set to keep track of accounts related to pinned accounts */ - Set relatedAccounts = new HashSet<>(); + Map relatedAccounts = new HashMap<>(); for (AccountDeviceInstanceKey adiKey : pinnedAccountModel.getPinnedAccounts()) { if (isCancelled()) { break; } List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); - relatedAccounts.add(adiKey); + relatedAccounts.put(adiKey.getAccountDeviceInstance().getAccount().getAccountID(), adiKey); getOrCreateVertex(adiKey); //get accounts related to pinned account for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { -// handle.progress(1); long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); final AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); - relatedAccounts.add(relatedADIKey); //store related accounts + relatedAccounts.put(relatedADI.getAccount().getAccountID(), relatedADIKey); //store related accounts } progress.progress(++i); } - //for each pair of related accounts add edges if they are related o each other. - // this is O(n^2) in the number of related accounts!!! - List relatedAccountsList = new ArrayList<>(relatedAccounts); - progress.switchToDeterminate("", 0, relatedAccountsList.size()); - for (i = 0; i < relatedAccountsList.size(); i++) { - AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); - for (int j = i; j < relatedAccountsList.size(); j++) { - if (isCancelled()) { - break; - } - AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); - List relationships = commsManager.getRelationshipSources( - adiKey1.getAccountDeviceInstance(), - adiKey2.getAccountDeviceInstance(), - currentFilter); - if (relationships.size() > 0) { - mxCell addEdge = addEdge(relationships, adiKey1, adiKey2); - progress.progress(addEdge.getId()); - } - } - progress.progress(i); + Set accountIDs = relatedAccounts.keySet(); + + Map relationshipCounts = commsManager.getRelationshipCounts(accountIDs, currentFilter); + + int total = relationshipCounts.size(); + int k = 0; + progress.switchToDeterminate("", 0,total); + for (Map.Entry entry : relationshipCounts.entrySet()) { + Long count = entry.getValue(); + Relationship.RelationshipKey relationshipKey = entry.getKey(); + AccountDeviceInstanceKey account1 = relatedAccounts.get(relationshipKey.getAccount1ID()); + AccountDeviceInstanceKey account2 = relatedAccounts.get(relationshipKey.getAccount2ID()); + mxCell addEdge = addOrUpdateEdge(count, account1, account2); + progress.progress(addEdge.getId(),k++); } +// //for each pair of related accounts add edges if they are related o each other. +// // this is O(n^2) in the number of related accounts!!! +// List relatedAccountsList = new ArrayList<>(relatedAccounts); +// progress.switchToDeterminate("", 0, relatedAccountsList.size()); +// +// for (i = 0; i < relatedAccountsList.size(); i++) { +// AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); +// for (int j = i; j < relatedAccountsList.size(); j++) { +// if (isCancelled()) { +// break; +// } +// AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); +// List relationships = commsManager.getRelationshipSources( +// adiKey1.getAccountDeviceInstance(), +// adiKey2.getAccountDeviceInstance(), +// currentFilter); +// if (relationships.size() > 0) { +// mxCell addEdge = addEdge(relationships, adiKey1, adiKey2); +// progress.progress(addEdge.getId()); +// } +// } +// progress.progress(i); +// } } catch (TskCoreException tskCoreException) { logger.log(Level.SEVERE, "Error", tskCoreException); } finally { diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 12b66824e6..889b81cf74 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -53,7 +53,7 @@ import java.util.Arrays; import static java.util.Collections.singleton; import java.util.EnumSet; import java.util.HashSet; -import java.util.Set; +import java.util.List; import java.util.concurrent.Future; import java.util.logging.Level; import javax.swing.AbstractAction; @@ -723,11 +723,23 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider HashSet adis = new HashSet<>(); for (mxICell cell : selectedCells) { if (cell.isEdge()) { - relationshipSources.addAll((Set) cell.getValue()); + mxICell source = (mxICell) graph.getModel().getTerminal(cell, true); + AccountDeviceInstanceKey account1 = (AccountDeviceInstanceKey) source.getValue(); + mxICell target = (mxICell) graph.getModel().getTerminal(cell, false); + AccountDeviceInstanceKey account2 = (AccountDeviceInstanceKey) target.getValue(); + try { + final List relationshipSources1 = commsManager.getRelationshipSources(account1.getAccountDeviceInstance(), + account2.getAccountDeviceInstance(), + currentFilter); + relationshipSources.addAll(relationshipSources1); + } catch (TskCoreException tskCoreException) { + logger.log(Level.SEVERE, " Error getting relationsips....", tskCoreException); + } } else if (cell.isVertex()) { adis.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance()); } } + rootNode = SelectionNode.createFromAccountsAndRelationships(relationshipSources, adis, currentFilter, commsManager); selectedNodes = new Node[]{rootNode}; }