Merge remote-tracking branch 'origin/GP-6761_ghidragon_append_graph_bug--SQUASHED'

This commit is contained in:
Ryan Kurtz
2026-04-29 07:05:12 -04:00
9 changed files with 91 additions and 30 deletions
@@ -4,9 +4,9 @@
* 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.
@@ -151,7 +151,7 @@ public class GraphDisplayBrokerPlugin extends Plugin
public GraphDisplay getDefaultGraphDisplay(boolean reuseGraph, TaskMonitor monitor)
throws GraphException {
if (defaultGraphDisplayProvider != null) {
return defaultGraphDisplayProvider.getGraphDisplay(reuseGraph, monitor);
return defaultGraphDisplayProvider.getGraphDisplay(reuseGraph, false, monitor);
}
return null;
}
@@ -4,9 +4,9 @@
* 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.
@@ -52,8 +52,7 @@ public class ExportAttributedGraphDisplayProvider implements GraphDisplayProvide
}
@Override
public GraphDisplay getGraphDisplay(boolean reuseGraph,
TaskMonitor monitor) {
public GraphDisplay getGraphDisplay(boolean reuseGraph, boolean append, TaskMonitor monitor) {
return new ExportAttributedGraphDisplay(this);
}
@@ -568,7 +568,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
}
private void createAndDisplaySubGraph() {
GraphDisplay display = graphDisplayProvider.getGraphDisplay(false, TaskMonitor.DUMMY);
GraphDisplay display =
graphDisplayProvider.getGraphDisplay(false, false, TaskMonitor.DUMMY);
try {
display.setGraph(createSubGraph(), graphRenderer.getGraphDisplayOptions(),
title + " - Sub-graph", false, TaskMonitor.DUMMY);
@@ -1065,7 +1066,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
public void setGraph(AttributedGraph graph, GraphDisplayOptions options, String title,
boolean append, TaskMonitor monitor) {
setGraphDisplayOptions(options);
if (append && Objects.equals(title, this.title) && this.graph != null) {
if (append && this.graph != null &&
graph.getGraphType().equals(this.graph.getGraphType())) {
graph = mergeGraphs(graph, this.graph);
}
@@ -4,9 +4,9 @@
* 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.
@@ -52,7 +52,7 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
}
@Override
public GraphDisplay getGraphDisplay(boolean reuseGraph, TaskMonitor monitor) {
public GraphDisplay getGraphDisplay(boolean reuseGraph, boolean append, TaskMonitor monitor) {
return Swing.runNow(() -> {
@@ -61,9 +61,15 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
(DefaultGraphDisplayWrapper) getActiveGraphDisplay();
// set a temporary dummy graph; clients will set a real graph
visibleGraph.setGraph(new AttributedGraph("Empty", null),
new DefaultGraphDisplayOptions(), "", false, monitor);
visibleGraph.restoreDefaultState();
if (!append) {
// only clear the graph if we aren't appending. We did the clear in case
// it was laying out the graph and nodes got yanked out from under it, but
// if the intention is to append to the graph, all existing nodes will still
// be there, so no stack trace attempting to access a missing vertex.
visibleGraph.setGraph(new AttributedGraph("Empty", null),
new DefaultGraphDisplayOptions(), "", false, monitor);
visibleGraph.restoreDefaultState();
}
return visibleGraph;
}
@@ -4,9 +4,9 @@
* 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.
@@ -93,7 +93,7 @@ public class BlockGraphTask extends Task {
AttributedGraph graph = createGraph(graphTitle);
monitor.setMessage("Generating Graph...");
try {
GraphDisplay display = graphProvider.getGraphDisplay(reuseGraph, monitor);
GraphDisplay display = graphProvider.getGraphDisplay(reuseGraph, appendGraph, monitor);
GraphDisplayOptions graphOptions = new ProgramGraphDisplayOptions(graphType, tool);
if (showCode) { // arrows need to be bigger as this generates larger vertices
graphOptions.setArrowLength(30);
@@ -4,9 +4,9 @@
* 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.
@@ -130,7 +130,7 @@ public class DataReferenceGraphTask extends Task {
try {
if (display == null) {
display = graphProvider.getGraphDisplay(reuseGraph, monitor);
display = graphProvider.getGraphDisplay(reuseGraph, appendGraph, monitor);
DataReferenceGraphDisplayListener listener =
new DataReferenceGraphDisplayListener(tool, display, program, totalMaxDepth);
@@ -4,9 +4,9 @@
* 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.
@@ -38,12 +38,34 @@ public interface GraphDisplayProvider extends ExtensionPoint {
/**
* Returns a GraphDisplay that can be used to "display" a graph
*
* @param reuseGraph if true, this provider will attempt to re-use an existing GraphDisplay
* @param reuseGraph if true, this provider will attempt to re-use an existing GraphDisplay.
* Note: this form will always clear the graph. If the intention is to append to the graph,
* use the {@link #getGraphDisplay(boolean, boolean, TaskMonitor)} method instead.
* so that it can be appended to. Otherwise, any reused graph display will be have its
* existing graph cleared.
*
* @param monitor the {@link TaskMonitor} that can be used to monitor and cancel the operation
* @return an object that can be used to display or otherwise consume (e.g., export) the graph
* @throws GraphException thrown if there is a problem creating a GraphDisplay
*/
public GraphDisplay getGraphDisplay(boolean reuseGraph, TaskMonitor monitor)
public default GraphDisplay getGraphDisplay(boolean reuseGraph, TaskMonitor monitor)
throws GraphException {
return getGraphDisplay(reuseGraph, false, monitor);
}
/**
* Returns a GraphDisplay that can be used to "display" a graph
*
* @param reuseGraph if true, this provider will attempt to re-use an existing GraphDisplay
* @param append if true and there is a graph display to reuse, don't clear the existing graph
* so that it can be appended to. Otherwise, any reused graph display will be have its
* existing graph cleared.
*
* @param monitor the {@link TaskMonitor} that can be used to monitor and cancel the operation
* @return an object that can be used to display or otherwise consume (e.g., export) the graph
* @throws GraphException thrown if there is a problem creating a GraphDisplay
*/
public GraphDisplay getGraphDisplay(boolean reuseGraph, boolean append, TaskMonitor monitor)
throws GraphException;
/**
@@ -4,9 +4,9 @@
* 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.
@@ -35,7 +35,7 @@ public class TestGraphService implements GraphDisplayProvider {
}
@Override
public GraphDisplay getGraphDisplay(boolean reuseGraph,
public GraphDisplay getGraphDisplay(boolean reuseGraph, boolean append,
TaskMonitor monitor) throws GraphException {
return testDisplay;
}
@@ -4,9 +4,9 @@
* 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.
@@ -22,6 +22,8 @@ import java.util.Map;
import org.junit.Test;
import ghidra.graph.*;
import ghidra.graph.visualization.DefaultGraphDisplayProvider;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.block.CodeBlockModel;
import ghidra.program.util.ProgramSelection;
import ghidra.service.graph.*;
@@ -225,6 +227,36 @@ public class BlockGraphTaskTest extends AbstractBlockGraphTest {
assertNotNull(e3);
assertNotNull(e4);
assertNotNull(e5);
}
@Test
public void testAppendGraph() throws Exception {
String modelName = blockModelService.getActiveBlockModelName();
CodeBlockModel model = blockModelService.getNewModelByName(modelName, program, true);
ProgramSelection selection =
new ProgramSelection(new AddressSet(addr(0x1002200), addr(0x01002203)));
GraphDisplayProvider graphService = new DefaultGraphDisplayProvider();
runSwing(() -> graphService.initialize(tool, tool.getOptions("foo")));
BlockGraphTask task =
new BlockGraphTask(new BlockFlowGraphType(), false, false,
false, tool, selection, null, model, graphService);
task.monitoredRun(TaskMonitor.DUMMY);
waitForSwing();
GraphDisplay display = graphService.getActiveGraphDisplay();
AttributedGraph graph = display.getGraph();
assertEquals(2, graph.getVertexCount());
selection = new ProgramSelection(new AddressSet(addr(0x01002239)));
task = new BlockGraphTask(new BlockFlowGraphType(), false, true, true, tool, selection,
null, model, graphService);
task.monitoredRun(TaskMonitor.DUMMY);
waitForSwing();
graph = display.getGraph();
assertEquals(3, graph.getVertexCount());
}