mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GP-3402 - Updated the Graph service API to better manage concurrent
threaded accesses
This commit is contained in:
parent
de55e42686
commit
bde74ad4d3
12 changed files with 422 additions and 387 deletions
|
@ -56,11 +56,6 @@ public abstract class AddressBasedGraphDisplayListener
|
|||
program.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void graphClosed() {
|
||||
dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void locationFocusChanged(AttributedVertex vertex) {
|
||||
Address address = getAddress(vertex);
|
||||
|
|
|
@ -26,10 +26,10 @@ import ghidra.util.task.TaskMonitor;
|
|||
|
||||
/**
|
||||
* Ghidra service interface for managing and directing graph output. It purpose is to discover
|
||||
* available graphing display providers and (if more than one) allow the user to select the currently
|
||||
* active graph consumer. Clients that generate graphs don't have to worry about how to display them
|
||||
* or export graphs. They simply send their graphs to the broker and register for graph events if
|
||||
* they want interactive support.
|
||||
* available graphing display providers and (if more than one) allow the user to select the
|
||||
* currently active graph consumer. Clients that generate graphs don't have to worry about how to
|
||||
* display them or export graphs. They simply send their graphs to the broker and register for graph
|
||||
* events if they want interactive support.
|
||||
*/
|
||||
@ServiceInfo(defaultProvider = GraphDisplayBrokerPlugin.class, description = "Get a Graph Display")
|
||||
public interface GraphDisplayBroker {
|
||||
|
|
|
@ -17,13 +17,12 @@ package ghidra.graph.export;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import org.jgrapht.Graph;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.EventTrigger;
|
||||
import ghidra.app.services.GraphDisplayBroker;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.service.graph.*;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
@ -66,7 +65,8 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
|
|||
*/
|
||||
private void doSetGraphData(AttributedGraph attributedGraph) {
|
||||
List<AttributedGraphExporter> exporters = findGraphExporters();
|
||||
GraphExporterDialog dialog = new GraphExporterDialog(attributedGraph, exporters);
|
||||
GraphExporterDialog dialog =
|
||||
Swing.runNow(() -> new GraphExporterDialog(attributedGraph, exporters));
|
||||
tool.showDialog(dialog);
|
||||
}
|
||||
|
||||
|
@ -79,8 +79,7 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setGraph(AttributedGraph graph, String title, boolean append,
|
||||
TaskMonitor monitor) {
|
||||
public void setGraph(AttributedGraph graph, String title, boolean append, TaskMonitor monitor) {
|
||||
this.title = title;
|
||||
this.graph = graph;
|
||||
doSetGraphData(graph);
|
||||
|
@ -92,9 +91,6 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
|
|||
this.setGraph(graph, title, append, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove all vertices and edges from the {@link Graph}
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
// not interactive, so N/A
|
||||
|
|
|
@ -258,8 +258,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
// this lens' delegate is the viewer's VIEW layer, abandoned above
|
||||
.delegate(transformer)
|
||||
.build();
|
||||
LensGraphMouse lensGraphMouse =
|
||||
DefaultLensGraphMouse.builder()
|
||||
LensGraphMouse lensGraphMouse = DefaultLensGraphMouse.builder()
|
||||
.magnificationFloor(1.f)
|
||||
.magnificationCeiling(60.f)
|
||||
.magnificationDelta(.2f)
|
||||
|
@ -831,10 +830,12 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
public void close() {
|
||||
graphDisplayProvider.remove(this);
|
||||
if (listener != null) {
|
||||
listener.graphClosed();
|
||||
}
|
||||
listener.dispose();
|
||||
listener = null;
|
||||
}
|
||||
|
||||
componentProvider.closeComponent();
|
||||
|
||||
if (graphDisplayOptions != null) {
|
||||
graphDisplayOptions.removeChangeListener(graphDisplayOptionsChangeListener);
|
||||
}
|
||||
|
@ -843,7 +844,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
@Override
|
||||
public void setGraphDisplayListener(GraphDisplayListener listener) {
|
||||
if (this.listener != null) {
|
||||
this.listener.graphClosed();
|
||||
// This is a bit odd to do here, but this seems like the easiest way to cleanup any
|
||||
// previous listener when reusing the graph display.
|
||||
this.listener.dispose();
|
||||
}
|
||||
this.listener = listener;
|
||||
}
|
||||
|
@ -975,12 +978,10 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
|
||||
configureViewerPreferredSize();
|
||||
|
||||
Swing.runNow(() -> {
|
||||
// set the graph but defer the layout algorithm setting
|
||||
viewer.getVisualizationModel().setGraph(graph, false);
|
||||
configureFilters();
|
||||
setInitialLayoutAlgorithm();
|
||||
});
|
||||
componentProvider.setVisible(true);
|
||||
}
|
||||
|
||||
|
@ -1310,7 +1311,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
|||
}
|
||||
|
||||
addedActions.add(action);
|
||||
Swing.runLater(() -> componentProvider.addLocalAction(action));
|
||||
componentProvider.addLocalAction(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package ghidra.graph.visualization;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.framework.options.Options;
|
||||
|
@ -30,7 +31,7 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
|||
|
||||
private static final String PREFERENCES_KEY = "GRAPH_DISPLAY_SERVICE";
|
||||
private static final String DEFAULT_SATELLITE_STATE = "DEFAULT_SATELLITE_STATE";
|
||||
private final Set<DefaultGraphDisplay> displays = new HashSet<>();
|
||||
private final Set<DefaultGraphDisplayWrapper> displays = new CopyOnWriteArraySet<>();
|
||||
private PluginTool pluginTool;
|
||||
private Options options;
|
||||
private int displayCounter = 1;
|
||||
|
@ -52,16 +53,22 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
|||
|
||||
@Override
|
||||
public GraphDisplay getGraphDisplay(boolean reuseGraph, TaskMonitor monitor) {
|
||||
|
||||
return Swing.runNow(() -> {
|
||||
|
||||
if (reuseGraph && !displays.isEmpty()) {
|
||||
DefaultGraphDisplay visibleGraph = (DefaultGraphDisplay) getActiveGraphDisplay();
|
||||
DefaultGraphDisplayWrapper visibleGraph =
|
||||
(DefaultGraphDisplayWrapper) getActiveGraphDisplay();
|
||||
|
||||
// set a temporary dummy graph; clients will set a real graph
|
||||
visibleGraph.setGraph(new AttributedGraph("Empty", null),
|
||||
new DefaultGraphDisplayOptions(), "", false, monitor);
|
||||
visibleGraph.restoreToDefaultSetOfActions();
|
||||
visibleGraph.restoreDefaultState();
|
||||
return visibleGraph;
|
||||
}
|
||||
|
||||
DefaultGraphDisplay display = new DefaultGraphDisplay(this, displayCounter++);
|
||||
DefaultGraphDisplayWrapper display =
|
||||
new DefaultGraphDisplayWrapper(this, displayCounter++);
|
||||
displays.add(display);
|
||||
return display;
|
||||
});
|
||||
|
@ -72,27 +79,28 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
|||
if (displays.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// get the sorted displays in order to pick the newest graph
|
||||
return getAllGraphDisplays().get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GraphDisplay> getAllGraphDisplays() {
|
||||
return Swing.runNow(() -> {
|
||||
return displays.stream()
|
||||
.sorted((d1, d2) -> -(d1.getId() - d2.getId())) // largest/newest IDs come first
|
||||
.collect(Collectors.toList());
|
||||
});
|
||||
return displays.stream().sorted().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(PluginTool tool, Options graphOptions) {
|
||||
this.pluginTool = tool;
|
||||
this.options = graphOptions;
|
||||
|
||||
Swing.assertSwingThread("Graph preferences must be accessed on the Swing thread");
|
||||
preferences = pluginTool.getWindowManager().getPreferenceState(PREFERENCES_KEY);
|
||||
if (preferences == null) {
|
||||
preferences = new PreferenceState();
|
||||
pluginTool.getWindowManager().putPreferenceState(PREFERENCES_KEY, preferences);
|
||||
}
|
||||
|
||||
defaultSatelliteState = preferences.getBoolean(DEFAULT_SATELLITE_STATE, false);
|
||||
}
|
||||
|
||||
|
@ -103,11 +111,12 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
|||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// first copy to new set to avoid concurrent modification exception
|
||||
HashSet<DefaultGraphDisplay> set = new HashSet<>(displays);
|
||||
for (DefaultGraphDisplay display : set) {
|
||||
display.close();
|
||||
}
|
||||
|
||||
// Calling close() will trigger the display to call back to this class's remove(). Avoid
|
||||
// unnecessary copies in the 'copy on write' set by closing after clearing the set.
|
||||
Set<GraphDisplay> set = new HashSet<>(displays);
|
||||
displays.clear();
|
||||
set.forEach(d -> d.close());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -115,8 +124,8 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
|||
return new HelpLocation("GraphServices", "Default_Graph_Display");
|
||||
}
|
||||
|
||||
public void remove(DefaultGraphDisplay defaultGraphDisplay) {
|
||||
displays.remove(defaultGraphDisplay);
|
||||
void remove(DefaultGraphDisplay defaultGraphDisplay) {
|
||||
displays.removeIf(wrapper -> wrapper.isDelegate(defaultGraphDisplay));
|
||||
}
|
||||
|
||||
boolean getDefaultSatelliteState() {
|
||||
|
@ -124,9 +133,8 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
|||
}
|
||||
|
||||
void setDefaultSatelliteState(boolean b) {
|
||||
Swing.assertSwingThread("Graph preferences must be accessed on the Swing thread");
|
||||
defaultSatelliteState = b;
|
||||
preferences.putBoolean(DEFAULT_SATELLITE_STATE, b);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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 ghidra.graph.visualization;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.EventTrigger;
|
||||
import ghidra.service.graph.*;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A {@link DefaultGraphDisplay} wrapper created to ensure all accesses to the delegate are on the
|
||||
* Swing thread. This API is meant to be used concurrently. We do not want to force clients to
|
||||
* have to understand when to use the Swing thread. Thus, this class handles that for the clients.
|
||||
* Also, by having Swing accesses managed here, the {@link DefaultGraphDisplay} can assume that all
|
||||
* its work will be done on the Swing thread.
|
||||
*/
|
||||
public class DefaultGraphDisplayWrapper
|
||||
implements GraphDisplay, Comparable<DefaultGraphDisplayWrapper> {
|
||||
|
||||
private DefaultGraphDisplay delegate;
|
||||
|
||||
DefaultGraphDisplayWrapper(DefaultGraphDisplayProvider displayProvider, int id) {
|
||||
delegate = Swing.runNow(() -> new DefaultGraphDisplay(displayProvider, id));
|
||||
}
|
||||
|
||||
void restoreDefaultState() {
|
||||
Swing.runNow(() -> delegate.restoreToDefaultSetOfActions());
|
||||
}
|
||||
|
||||
boolean isDelegate(DefaultGraphDisplay other) {
|
||||
return other == delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGraphDisplayListener(GraphDisplayListener listener) {
|
||||
Swing.runNow(() -> delegate.setGraphDisplayListener(listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFocusedVertex(AttributedVertex vertex, EventTrigger eventTrigger) {
|
||||
Swing.runNow(() -> delegate.setFocusedVertex(vertex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributedGraph getGraph() {
|
||||
return Swing.runNow(() -> delegate.getGraph());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributedVertex getFocusedVertex() {
|
||||
return Swing.runNow(() -> delegate.getFocusedVertex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectVertices(Set<AttributedVertex> vertexSet, EventTrigger eventTrigger) {
|
||||
Swing.runNow(() -> delegate.selectVertices(vertexSet, eventTrigger));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AttributedVertex> getSelectedVertices() {
|
||||
return Swing.runNow(() -> delegate.getSelectedVertices());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Swing.runNow(() -> delegate.close());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGraph(AttributedGraph graph, GraphDisplayOptions options, String title,
|
||||
boolean append, TaskMonitor monitor) {
|
||||
Swing.runNow(() -> delegate.setGraph(graph, options, title, append, monitor));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
Swing.runNow(() -> delegate.clear());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateVertexName(AttributedVertex vertex, String newName) {
|
||||
Swing.runNow(() -> delegate.updateVertexName(vertex, newName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGraphTitle() {
|
||||
return Swing.runNow(() -> delegate.getGraphTitle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAction(DockingActionIf action) {
|
||||
Swing.runNow(() -> delegate.addAction(action));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(DefaultGraphDisplayWrapper other) {
|
||||
// note: no need for call to Swing, assuming ID is immutable
|
||||
|
||||
// larger/newer values are preferred so they should be first when sorting
|
||||
return -(delegate.getId() - other.delegate.getId());
|
||||
}
|
||||
}
|
|
@ -21,7 +21,6 @@ import java.util.*;
|
|||
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.widgets.EventTrigger;
|
||||
import ghidra.app.plugin.core.colorizer.ColorizingService;
|
||||
import ghidra.app.util.AddEditDialog;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.graph.*;
|
||||
|
@ -54,8 +53,6 @@ public class BlockGraphTask extends Task {
|
|||
private boolean showCode = false;
|
||||
private int codeLimitPerBlock = 10;
|
||||
|
||||
private ColorizingService colorizingService;
|
||||
|
||||
private final static String ENTRY_NEXUS_NAME = "Entry Points";
|
||||
private static final int MAX_SYMBOLS = 10;
|
||||
private CodeBlockModel blockModel;
|
||||
|
@ -70,10 +67,10 @@ public class BlockGraphTask extends Task {
|
|||
private String graphTitle;
|
||||
private ProgramGraphType graphType;
|
||||
|
||||
public BlockGraphTask(ProgramGraphType graphType,
|
||||
boolean graphEntryPointNexus, boolean reuseGraph, boolean appendGraph,
|
||||
PluginTool tool, ProgramSelection selection, ProgramLocation location,
|
||||
CodeBlockModel blockModel, GraphDisplayProvider graphProvider) {
|
||||
public BlockGraphTask(ProgramGraphType graphType, boolean graphEntryPointNexus,
|
||||
boolean reuseGraph, boolean appendGraph, PluginTool tool, ProgramSelection selection,
|
||||
ProgramLocation location, CodeBlockModel blockModel,
|
||||
GraphDisplayProvider graphProvider) {
|
||||
|
||||
super("Graph Program", true, false, true);
|
||||
this.graphType = graphType;
|
||||
|
@ -84,24 +81,19 @@ public class BlockGraphTask extends Task {
|
|||
this.tool = tool;
|
||||
this.blockModel = blockModel;
|
||||
this.graphProvider = graphProvider;
|
||||
this.colorizingService = tool.getService(ColorizingService.class);
|
||||
this.selection = selection;
|
||||
this.location = location;
|
||||
this.program = blockModel.getProgram();
|
||||
this.graphTitle = graphType.getName() + ": ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the move memory operation.
|
||||
*/
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
this.graphScope = getGraphScopeAndGenerateGraphTitle();
|
||||
AttributedGraph graph = createGraph(graphTitle);
|
||||
monitor.setMessage("Generating Graph...");
|
||||
try {
|
||||
GraphDisplay display =
|
||||
graphProvider.getGraphDisplay(reuseGraph, monitor);
|
||||
GraphDisplay display = graphProvider.getGraphDisplay(reuseGraph, monitor);
|
||||
GraphDisplayOptions graphOptions = new ProgramGraphDisplayOptions(graphType, tool);
|
||||
if (showCode) { // arrows need to be bigger as this generates larger vertices
|
||||
graphOptions.setArrowLength(30);
|
||||
|
@ -140,8 +132,8 @@ public class BlockGraphTask extends Task {
|
|||
private void addActions(GraphDisplay display,
|
||||
java.util.function.Function<AttributedVertex, Address> addressFunction) {
|
||||
|
||||
display.addAction(new ActionBuilder("Rename Symbol", "Block Graph")
|
||||
.popupMenuPath("Rename Symbol")
|
||||
display.addAction(
|
||||
new ActionBuilder("Rename Symbol", "Block Graph").popupMenuPath("Rename Symbol")
|
||||
.withContext(VertexGraphActionContext.class)
|
||||
.helpLocation(new HelpLocation("ProgramGraphPlugin", "Rename_Symbol"))
|
||||
// only enable action when vertex corresponds to an address
|
||||
|
@ -264,8 +256,7 @@ public class BlockGraphTask extends Task {
|
|||
}
|
||||
|
||||
private Address graphBlock(AttributedGraph graph, CodeBlock curBB,
|
||||
List<AttributedVertex> entries)
|
||||
throws CancelledException {
|
||||
List<AttributedVertex> entries) throws CancelledException {
|
||||
|
||||
Address[] startAddrs = curBB.getStartAddresses();
|
||||
|
||||
|
|
|
@ -124,16 +124,16 @@ public class AttributedGraph extends AbstractBaseGraph<AttributedVertex, Attribu
|
|||
* then that vertex will be returned, but with its name changed to the given name.
|
||||
*
|
||||
* @param id the unique vertex id that the graph should have a vertex for.
|
||||
* @param name the name to associate with this vertex
|
||||
* @param vertexName the name to associate with this vertex
|
||||
* @return either an existing vertex with that id, or a newly added vertex with that id
|
||||
*/
|
||||
public AttributedVertex addVertex(String id, String name) {
|
||||
public AttributedVertex addVertex(String id, String vertexName) {
|
||||
if (vertexMap.containsKey(id)) {
|
||||
AttributedVertex vertex = vertexMap.get(id);
|
||||
vertex.setName(name);
|
||||
vertex.setName(vertexName);
|
||||
return vertex;
|
||||
}
|
||||
AttributedVertex newVertex = new AttributedVertex(id, name);
|
||||
AttributedVertex newVertex = new AttributedVertex(id, vertexName);
|
||||
addVertex(newVertex);
|
||||
return newVertex;
|
||||
}
|
||||
|
|
|
@ -19,11 +19,6 @@ import java.util.Set;
|
|||
|
||||
public class DummyGraphDisplayListener implements GraphDisplayListener {
|
||||
|
||||
@Override
|
||||
public void graphClosed() {
|
||||
// I'm a dummy
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphDisplayListener cloneWith(GraphDisplay graphDisplay) {
|
||||
return new DummyGraphDisplayListener();
|
||||
|
@ -31,17 +26,17 @@ public class DummyGraphDisplayListener implements GraphDisplayListener {
|
|||
|
||||
@Override
|
||||
public void selectionChanged(Set<AttributedVertex> vertices) {
|
||||
// I'm a dummy
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void locationFocusChanged(AttributedVertex vertex) {
|
||||
// I'm a dummy
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// I'm a dummy
|
||||
// stub
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,62 +30,6 @@ import ghidra.util.task.TaskMonitor;
|
|||
* closed.
|
||||
*/
|
||||
public interface GraphDisplay {
|
||||
public static final int ALIGN_LEFT = 0; // aligns graph text to the left
|
||||
public static final int ALIGN_CENTER = 1; // aligns graph text to the center
|
||||
public static final int ALIGN_RIGHT = 2; // aligns graph text to the right
|
||||
|
||||
/**
|
||||
* values are color names or rgb in hex '0xFF0000' is red
|
||||
*/
|
||||
public static final String SELECTED_VERTEX_COLOR = "selectedVertexColor";
|
||||
|
||||
/**
|
||||
* values are color names or rgb in hex '0xFF0000' is red
|
||||
*/
|
||||
public static final String SELECTED_EDGE_COLOR = "selectedEdgeColor";
|
||||
|
||||
/**
|
||||
* values are defined as String symbols in LayoutFunction class
|
||||
*
|
||||
* KAMADA_KAWAI,FRUCTERMAN_REINGOLD,CIRCLE_MINCROSS,TIDIER_TREE,TIDIER_RADIAL_TREE,
|
||||
* MIN_CROSS_TOP_DOWN,MIN_CROSS_LONGEST_PATH,MIN_CROSS_NETWORK_SIMPLEX,MIN_CROSS_COFFMAN_GRAHAM,
|
||||
* EXP_MIN_CROSS_TOP_DOWN,EXP_MIN_CROSS_LONGEST_PATH,EXP_MIN_CROSS_NETWORK_SIMPLEX,
|
||||
* EXP_MIN_CROSS_COFFMAN_GRAHAM,TREE,RADIAL,BALLOON,GEM
|
||||
*
|
||||
* may have no meaning for a different graph visualization library
|
||||
*/
|
||||
public static final String INITIAL_LAYOUT_ALGORITHM = "initialLayoutAlgorithm";
|
||||
|
||||
/**
|
||||
* true or false
|
||||
* may have no meaning for a different graph visualization library
|
||||
*/
|
||||
public static final String DISPLAY_VERTICES_AS_ICONS = "displayVerticesAsIcons";
|
||||
|
||||
/**
|
||||
* values are the strings N,NE,E,SE,S,SW,W,NW,AUTO,CNTR
|
||||
* may have no meaning for a different graph visualization library
|
||||
*/
|
||||
public static final String VERTEX_LABEL_POSITION = "vertexLabelPosition";
|
||||
|
||||
/**
|
||||
* true or false, whether edge selection via a mouse click is enabled.
|
||||
* May not be supported by another graph visualization library
|
||||
*/
|
||||
public static final String ENABLE_EDGE_SELECTION = "enableEdgeSelection";
|
||||
|
||||
/**
|
||||
* a comma-separated list of edge type names in priority order
|
||||
*/
|
||||
public static final String EDGE_TYPE_PRIORITY_LIST = "edgeTypePriorityList";
|
||||
|
||||
/**
|
||||
* a comma-separated list of edge type names.
|
||||
* any will be considered a favored edge for the min-cross layout
|
||||
* algorithms.
|
||||
* May have no meaning with a different graph visualization library
|
||||
*/
|
||||
public static final String FAVORED_EDGES = "favoredEdges";
|
||||
|
||||
/**
|
||||
* Sets a {@link GraphDisplayListener} to be notified when the user changes the vertex focus
|
||||
|
@ -156,6 +100,7 @@ public interface GraphDisplay {
|
|||
* @throws CancelledException thrown if the graphing operation was cancelled
|
||||
* @deprecated You should now use the form that takes in a {@link GraphDisplayOptions}
|
||||
*/
|
||||
@Deprecated
|
||||
public default void setGraph(AttributedGraph graph, String title, boolean append,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
setGraph(graph, new GraphDisplayOptions(graph.getGraphType()), title, append, monitor);
|
||||
|
|
|
@ -21,10 +21,6 @@ import java.util.Set;
|
|||
* Interface for being notified when the user interacts with a visual graph display
|
||||
*/
|
||||
public interface GraphDisplayListener {
|
||||
/**
|
||||
* Notification that the graph window has been closed
|
||||
*/
|
||||
public void graphClosed();
|
||||
|
||||
/**
|
||||
* Notification that the set of selected vertices has changed
|
||||
|
@ -50,7 +46,8 @@ public interface GraphDisplayListener {
|
|||
public GraphDisplayListener cloneWith(GraphDisplay graphDisplay);
|
||||
|
||||
/**
|
||||
* Tells the listener that it is no longer needed and it can release any listeners/resources
|
||||
* Tells the listener that it is no longer needed and it can release any listeners/resources.
|
||||
* This will be called when a {@link GraphDisplay} is disposed or if this listener is replaced.
|
||||
*/
|
||||
public void dispose();
|
||||
|
||||
|
|
|
@ -72,9 +72,8 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertTrue(display.getSelectedVertices().isEmpty());
|
||||
|
||||
DockingActionIf action = getAction(tool, "Select Vertex");
|
||||
VertexGraphActionContext context =
|
||||
new VertexGraphActionContext(graphComponentProvider, graph, null, null,
|
||||
graph.getVertex("B"));
|
||||
VertexGraphActionContext context = new VertexGraphActionContext(graphComponentProvider,
|
||||
graph, null, null, graph.getVertex("B"));
|
||||
performAction(action, context, true);
|
||||
|
||||
Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
|
||||
|
@ -102,9 +101,8 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertEquals(4, display.getSelectedVertices().size());
|
||||
|
||||
DockingActionIf action = getAction(tool, "Deselect Vertex");
|
||||
VertexGraphActionContext context =
|
||||
new VertexGraphActionContext(graphComponentProvider, graph, null, null,
|
||||
graph.getVertex("B"));
|
||||
VertexGraphActionContext context = new VertexGraphActionContext(graphComponentProvider,
|
||||
graph, null, null, graph.getVertex("B"));
|
||||
performAction(action, context, true);
|
||||
|
||||
Set<AttributedVertex> selected = display.getSelectedVertices();
|
||||
|
@ -121,9 +119,8 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertTrue(display.getSelectedVertices().isEmpty());
|
||||
|
||||
DockingActionIf action = getAction(tool, "Select Edge");
|
||||
EdgeGraphActionContext context =
|
||||
new EdgeGraphActionContext(graphComponentProvider, graph, null, null,
|
||||
graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
|
||||
EdgeGraphActionContext context = new EdgeGraphActionContext(graphComponentProvider, graph,
|
||||
null, null, graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
|
||||
performAction(action, context, true);
|
||||
|
||||
Set<AttributedVertex> selectedVerticeIds = display.getSelectedVertices();
|
||||
|
@ -135,9 +132,8 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
@Test
|
||||
public void testDeSelectEdgeAction() {
|
||||
DockingActionIf action = getAction(tool, "Select Edge");
|
||||
EdgeGraphActionContext context =
|
||||
new EdgeGraphActionContext(graphComponentProvider, graph, null, null,
|
||||
graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
|
||||
EdgeGraphActionContext context = new EdgeGraphActionContext(graphComponentProvider, graph,
|
||||
null, null, graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
|
||||
performAction(action, context, true);
|
||||
|
||||
Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
|
||||
|
@ -155,9 +151,8 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testSelectEdgeSource() {
|
||||
setFocusedVertex(d);
|
||||
DockingActionIf action = getAction(tool, "Edge Source");
|
||||
EdgeGraphActionContext context =
|
||||
new EdgeGraphActionContext(graphComponentProvider, graph, null, null,
|
||||
graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
|
||||
EdgeGraphActionContext context = new EdgeGraphActionContext(graphComponentProvider, graph,
|
||||
null, null, graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
|
||||
performAction(action, context, true);
|
||||
|
||||
assertEquals(a, display.getFocusedVertex());
|
||||
|
@ -167,9 +162,8 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testSelectEdgeTarget() {
|
||||
setFocusedVertex(d);
|
||||
DockingActionIf action = getAction(tool, "Edge Target");
|
||||
EdgeGraphActionContext context =
|
||||
new EdgeGraphActionContext(graphComponentProvider, graph, null, null,
|
||||
graph.getEdge(a, b));
|
||||
EdgeGraphActionContext context = new EdgeGraphActionContext(graphComponentProvider, graph,
|
||||
null, null, graph.getEdge(a, b));
|
||||
performAction(action, context, true);
|
||||
|
||||
assertEquals(b, display.getFocusedVertex());
|
||||
|
@ -523,11 +517,6 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
private class TestGraphDisplayListener implements GraphDisplayListener {
|
||||
|
||||
@Override
|
||||
public void graphClosed() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectionChanged(Set<AttributedVertex> vertices) {
|
||||
graphSpy.setSelection(vertices);
|
||||
|
@ -545,7 +534,7 @@ public class GraphActionsTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// do nothing
|
||||
// stub
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue