Changed graph api to use vertex and edge object instead of ids.

This commit is contained in:
ghidravore 2020-10-09 14:27:29 -04:00
parent 3cd26120a3
commit 592b8a3cfc
14 changed files with 292 additions and 283 deletions

View file

@ -15,8 +15,7 @@
*/ */
package ghidra.app.plugin.core.graph; package ghidra.app.plugin.core.graph;
import java.util.List; import java.util.*;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import docking.widgets.EventTrigger; import docking.widgets.EventTrigger;
@ -29,8 +28,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.util.*; import ghidra.program.util.*;
import ghidra.service.graph.GraphDisplay; import ghidra.service.graph.*;
import ghidra.service.graph.GraphDisplayListener;
import ghidra.util.Swing; import ghidra.util.Swing;
/** /**
@ -40,7 +38,7 @@ public abstract class AddressBasedGraphDisplayListener
implements GraphDisplayListener, PluginEventListener, DomainObjectListener { implements GraphDisplayListener, PluginEventListener, DomainObjectListener {
protected PluginTool tool; protected PluginTool tool;
private GraphDisplay graphDisplay; protected GraphDisplay graphDisplay;
protected Program program; protected Program program;
private SymbolTable symbolTable; private SymbolTable symbolTable;
private String name; private String name;
@ -63,8 +61,8 @@ public abstract class AddressBasedGraphDisplayListener
} }
@Override @Override
public void locationFocusChanged(String vertexId) { public void locationFocusChanged(AttributedVertex vertex) {
Address address = getAddressForVertexId(vertexId); Address address = getAddress(vertex);
if (address != null) { if (address != null) {
ProgramLocation location = new ProgramLocation(program, address); ProgramLocation location = new ProgramLocation(program, address);
tool.firePluginEvent(new ProgramLocationPluginEvent(name, location, program)); tool.firePluginEvent(new ProgramLocationPluginEvent(name, location, program));
@ -72,8 +70,8 @@ public abstract class AddressBasedGraphDisplayListener
} }
@Override @Override
public void selectionChanged(List<String> vertexIds) { public void selectionChanged(Set<AttributedVertex> vertices) {
AddressSet addressSet = getAddressSetForVertices(vertexIds); AddressSet addressSet = getAddresses(vertices);
if (addressSet != null) { if (addressSet != null) {
ProgramSelection selection = new ProgramSelection(addressSet); ProgramSelection selection = new ProgramSelection(addressSet);
ProgramSelectionPluginEvent event = ProgramSelectionPluginEvent event =
@ -99,16 +97,16 @@ public abstract class AddressBasedGraphDisplayListener
ProgramLocationPluginEvent ev = (ProgramLocationPluginEvent) event; ProgramLocationPluginEvent ev = (ProgramLocationPluginEvent) event;
if (isMyProgram(ev.getProgram())) { if (isMyProgram(ev.getProgram())) {
ProgramLocation location = ev.getLocation(); ProgramLocation location = ev.getLocation();
String id = getVertexIdForAddress(location.getAddress()); AttributedVertex vertex = getVertex(location.getAddress());
// update graph location, but tell it not to send out event // update graph location, but tell it not to send out event
graphDisplay.setLocationFocus(id, EventTrigger.INTERNAL_ONLY); graphDisplay.setFocusedVertex(vertex, EventTrigger.INTERNAL_ONLY);
} }
} }
else if (event instanceof ProgramSelectionPluginEvent) { else if (event instanceof ProgramSelectionPluginEvent) {
ProgramSelectionPluginEvent ev = (ProgramSelectionPluginEvent) event; ProgramSelectionPluginEvent ev = (ProgramSelectionPluginEvent) event;
if (isMyProgram(ev.getProgram())) { if (isMyProgram(ev.getProgram())) {
ProgramSelection selection = ev.getSelection(); ProgramSelection selection = ev.getSelection();
List<String> selectedVertices = getVertices(selection); Set<AttributedVertex> selectedVertices = getVertices(selection);
if (selectedVertices != null) { if (selectedVertices != null) {
// since we are responding to an event, tell the GraphDisplay not to send event // since we are responding to an event, tell the GraphDisplay not to send event
graphDisplay.selectVertices(selectedVertices, EventTrigger.INTERNAL_ONLY); graphDisplay.selectVertices(selectedVertices, EventTrigger.INTERNAL_ONLY);
@ -117,7 +115,15 @@ public abstract class AddressBasedGraphDisplayListener
} }
} }
protected String getVertexIdForAddress(Address address) { public AttributedVertex getVertex(Address address) {
if (address == null) {
return null;
}
String id = getVertexId(address);
return graphDisplay.getGraph().getVertex(id);
}
protected String getVertexId(Address address) {
// vertex ids for external locations use symbol names since they don't have meaningful addresses. // vertex ids for external locations use symbol names since they don't have meaningful addresses.
if (address.isExternalAddress()) { if (address.isExternalAddress()) {
Symbol s = symbolTable.getPrimarySymbol(address); Symbol s = symbolTable.getPrimarySymbol(address);
@ -153,13 +159,16 @@ public abstract class AddressBasedGraphDisplayListener
} }
protected Address getAddressForVertexId(String vertexId) { protected Address getAddress(AttributedVertex vertex) {
return getAddress(vertexId); if (vertex == null) {
return null;
}
return getAddress(vertex.getId());
} }
protected abstract List<String> getVertices(AddressSetView selection); protected abstract Set<AttributedVertex> getVertices(AddressSetView selection);
protected abstract AddressSet getAddressSetForVertices(List<String> vertexIds); protected abstract AddressSet getAddresses(Set<AttributedVertex> vertexIds);
private boolean isMyProgram(Program p) { private boolean isMyProgram(Program p) {
return p == program; return p == program;
@ -192,15 +201,18 @@ public abstract class AddressBasedGraphDisplayListener
} }
private void handleSymbolAddedOrRenamed(Address address, Symbol symbol) { private void handleSymbolAddedOrRenamed(Address address, Symbol symbol) {
String id = getVertexIdForAddress(address); AttributedVertex vertex = getVertex(address);
graphDisplay.updateVertexName(id, symbol.getName()); graphDisplay.updateVertexName(vertex, symbol.getName());
} }
private void handleSymbolRemoved(Address address) { private void handleSymbolRemoved(Address address) {
String id = getVertexIdForAddress(address); AttributedVertex vertex = getVertex(address);
if (vertex == null) {
return;
}
Symbol symbol = program.getSymbolTable().getPrimarySymbol(address); Symbol symbol = program.getSymbolTable().getPrimarySymbol(address);
String displayName = symbol == null ? address.toString() : symbol.getName(); String displayName = symbol == null ? address.toString() : symbol.getName();
graphDisplay.updateVertexName(id, displayName); graphDisplay.updateVertexName(vertex, displayName);
} }
private void dispose() { private void dispose() {

View file

@ -232,16 +232,15 @@ public class GraphAST extends GhidraScript {
} }
@Override @Override
protected List<String> getVertices(AddressSetView selection) { protected Set<AttributedVertex> getVertices(AddressSetView selection) {
List<String> ids = new ArrayList<String>(); return Collections.emptySet();
return ids;
} }
@Override @Override
protected AddressSet getAddressSetForVertices(List<String> vertexIds) { protected AddressSet getAddresses(Set<AttributedVertex> vertices) {
AddressSet set = new AddressSet(); AddressSet set = new AddressSet();
for (String id : vertexIds) { for (AttributedVertex vertex : vertices) {
Address address = getAddressForVertexId(id); Address address = getAddress(vertex);
if (address != null) { if (address != null) {
set.add(address); set.add(address);
} }
@ -250,7 +249,11 @@ public class GraphAST extends GhidraScript {
} }
@Override @Override
protected Address getAddressForVertexId(String vertexId) { protected Address getAddress(AttributedVertex vertex) {
if (vertex == null) {
return null;
}
String vertexId = vertex.getId();
int firstcolon = vertexId.indexOf(':'); int firstcolon = vertexId.indexOf(':');
if (firstcolon == -1) { if (firstcolon == -1) {
return null; return null;

View file

@ -17,8 +17,7 @@ package ghidra.app.plugin.core.decompile.actions;
import static ghidra.app.plugin.core.decompile.actions.ASTGraphTask.GraphType.*; import static ghidra.app.plugin.core.decompile.actions.ASTGraphTask.GraphType.*;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import ghidra.app.plugin.core.decompile.actions.ASTGraphTask.GraphType; import ghidra.app.plugin.core.decompile.actions.ASTGraphTask.GraphType;
import ghidra.app.plugin.core.graph.AddressBasedGraphDisplayListener; import ghidra.app.plugin.core.graph.AddressBasedGraphDisplayListener;
@ -26,8 +25,7 @@ import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.pcode.HighFunction; import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.PcodeBlockBasic; import ghidra.program.model.pcode.PcodeBlockBasic;
import ghidra.service.graph.GraphDisplay; import ghidra.service.graph.*;
import ghidra.service.graph.GraphDisplayListener;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
/** /**
@ -45,33 +43,37 @@ public class ASTGraphDisplayListener extends AddressBasedGraphDisplayListener {
} }
@Override @Override
protected List<String> getVertices(AddressSetView selection) { protected Set<AttributedVertex> getVertices(AddressSetView selection) {
if (graphType != CONTROL_FLOW_GRAPH) { if (graphType != CONTROL_FLOW_GRAPH) {
return null; return null;
} }
List<String> vertices = new ArrayList<>(); Set<AttributedVertex> vertices = new HashSet<>();
List<PcodeBlockBasic> blocks = hfunction.getBasicBlocks(); List<PcodeBlockBasic> blocks = hfunction.getBasicBlocks();
for (PcodeBlockBasic block : blocks) { for (PcodeBlockBasic block : blocks) {
Address start = block.getStart(); Address start = block.getStart();
Address stop = block.getStop(); Address stop = block.getStop();
if (selection.intersects(start, stop)) { if (selection.intersects(start, stop)) {
vertices.add(Integer.toString(block.getIndex())); String id = Integer.toString(block.getIndex());
AttributedVertex vertex = graphDisplay.getGraph().getVertex(id);
if (vertex != null) {
vertices.add(vertex);
}
} }
} }
return vertices; return vertices;
} }
@Override @Override
protected AddressSet getAddressSetForVertices(List<String> vertexIds) { protected AddressSet getAddresses(Set<AttributedVertex> vertices) {
if (graphType != CONTROL_FLOW_GRAPH) { if (graphType != CONTROL_FLOW_GRAPH) {
return null; return null;
} }
AddressSet set = new AddressSet(); AddressSet set = new AddressSet();
List<PcodeBlockBasic> blocks = hfunction.getBasicBlocks(); List<PcodeBlockBasic> blocks = hfunction.getBasicBlocks();
for (String vertixId : vertexIds) { for (AttributedVertex vertex : vertices) {
try { try {
int index = Integer.parseInt(vertixId); int index = Integer.parseInt(vertex.getId());
PcodeBlockBasic block = blocks.get(index); PcodeBlockBasic block = blocks.get(index);
Address start = block.getStart(); Address start = block.getStart();
set.addRange(start, block.getStop()); set.addRange(start, block.getStop());
@ -84,7 +86,7 @@ public class ASTGraphDisplayListener extends AddressBasedGraphDisplayListener {
} }
@Override @Override
protected String getVertexIdForAddress(Address address) { protected String getVertexId(Address address) {
if (graphType != CONTROL_FLOW_GRAPH) { if (graphType != CONTROL_FLOW_GRAPH) {
return null; return null;
} }
@ -96,25 +98,25 @@ public class ASTGraphDisplayListener extends AddressBasedGraphDisplayListener {
return Integer.toString(block.getIndex()); return Integer.toString(block.getIndex());
} }
} }
return super.getVertexIdForAddress(address); return super.getVertexId(address);
} }
@Override @Override
protected Address getAddressForVertexId(String vertexId) { protected Address getAddress(AttributedVertex vertex) {
List<PcodeBlockBasic> blocks = hfunction.getBasicBlocks(); List<PcodeBlockBasic> blocks = hfunction.getBasicBlocks();
try { try {
int index = Integer.parseInt(vertexId); int index = Integer.parseInt(vertex.getId());
PcodeBlockBasic block = blocks.get(index); PcodeBlockBasic block = blocks.get(index);
return block.getStart(); return block.getStart();
} }
catch (NumberFormatException e) { catch (NumberFormatException e) {
throw new AssertException("Bad vertex id, expected a number but got " + vertexId); throw new AssertException("Bad vertex id, expected a number but got " + vertex.getId());
} }
} }
@Override @Override
public GraphDisplayListener cloneWith(GraphDisplay graphDisplay) { public GraphDisplayListener cloneWith(GraphDisplay display) {
return new ASTGraphDisplayListener(tool, graphDisplay, hfunction, graphType); return new ASTGraphDisplayListener(tool, graphDisplay, hfunction, graphType);
} }

View file

@ -124,9 +124,9 @@ public class ASTGraphTask extends Task {
display.setGraph(graph, description, false, monitor); display.setGraph(graph, description, false, monitor);
// set the graph location // set the graph location
if (location != null) { if (location != null) {
String id = displayListener.getVertexIdForAddress(location); AttributedVertex vertex = displayListener.getVertex(location);
// update graph location, but don't have it send out event // update graph location, but don't have it send out event
display.setLocationFocus(id, EventTrigger.INTERNAL_ONLY); display.setFocusedVertex(vertex, EventTrigger.INTERNAL_ONLY);
} }
} }

View file

@ -15,7 +15,8 @@
*/ */
package ghidra.graph.export; package ghidra.graph.export;
import java.util.*; import java.util.Collections;
import java.util.Set;
import org.jgrapht.Graph; import org.jgrapht.Graph;
@ -57,15 +58,7 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
// This display is not interactive, so N/A // This display is not interactive, so N/A
} }
@Override
public void selectVertices(List<String> vertexList, EventTrigger eventTrigger) {
// This display is not interactive, so N/A
}
@Override
public void setLocationFocus(String vertexID, EventTrigger eventTrigger) {
// This display is not interactive, so N/A
}
/** /**
* set the {@link AttributedGraph} for visualization * set the {@link AttributedGraph} for visualization
@ -108,7 +101,7 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
} }
@Override @Override
public void updateVertexName(String id, String newName) { public void updateVertexName(AttributedVertex vertex, String newName) {
// do nothing // do nothing
} }
@ -123,13 +116,28 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
} }
@Override @Override
public String getFocusedVertexId() { public AttributedVertex getFocusedVertex() {
return null; return null;
} }
@Override @Override
public Set<String> getSelectedVertexIds() { public Set<AttributedVertex> getSelectedVertices() {
return Collections.emptySet(); return Collections.emptySet();
} }
@Override
public void setFocusedVertex(AttributedVertex vertex, EventTrigger eventTrigger) {
// not interactive, so N/A
}
@Override
public AttributedGraph getGraph() {
return null;
}
@Override
public void selectVertices(Set<AttributedVertex> vertexList, EventTrigger eventTrigger) {
// not interactive, so N/A
}
} }

View file

@ -539,9 +539,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
} }
}); });
Set<AttributedVertex> selected = selectedVertexState.getSelected(); Set<AttributedVertex> selected = selectedVertexState.getSelected();
List<String> selectedIds = notifySelectionChanged(selected);
selected.stream().map(AttributedVertex::getId).collect(Collectors.toList());
notifySelectionChanged(selectedIds);
} }
finally { finally {
switchableSelectionListener.setEnabled(true); switchableSelectionListener.setEnabled(true);
@ -716,17 +714,18 @@ public class DefaultGraphDisplay implements GraphDisplay {
setFocusedVertex(vertex, EventTrigger.API_CALL); setFocusedVertex(vertex, EventTrigger.API_CALL);
} }
protected void setFocusedVertex(AttributedVertex vertex, EventTrigger eventTrigger) { @Override
public void setFocusedVertex(AttributedVertex vertex, EventTrigger eventTrigger) {
boolean changed = this.focusedVertex != vertex; boolean changed = this.focusedVertex != vertex;
this.focusedVertex = vertex; this.focusedVertex = vertex;
if (focusedVertex != null) { if (focusedVertex != null) {
if (changed && eventTrigger != EventTrigger.INTERNAL_ONLY) { if (changed && eventTrigger != EventTrigger.INTERNAL_ONLY) {
notifyLocationFocusChanged(focusedVertex.getId()); notifyLocationFocusChanged(focusedVertex);
} }
// make sure the vertex is visible, even if the vertex has not changed // make sure the vertex is visible, even if the vertex has not changed
scrollToSelected(focusedVertex); scrollToSelected(focusedVertex);
viewer.repaint();
} }
viewer.repaint();
} }
/** /**
@ -769,22 +768,22 @@ public class DefaultGraphDisplay implements GraphDisplay {
/** /**
* fire an event to notify the selected vertices changed * fire an event to notify the selected vertices changed
* @param vertexIds the list of vertexes * @param selected the list of selected vertices
*/ */
private void notifySelectionChanged(List<String> vertexIds) { private void notifySelectionChanged(Set<AttributedVertex> selected) {
Swing.runLater(() -> listener.selectionChanged(vertexIds)); Swing.runLater(() -> listener.selectionChanged(selected));
} }
/** /**
* fire and event to say the focused vertex changed * fire and event to say the focused vertex changed
* @param vertexId the id of the focused vertex * @param vertex the new focused vertex
*/ */
private void notifyLocationFocusChanged(String vertexId) { private void notifyLocationFocusChanged(AttributedVertex vertex) {
Swing.runLater(() -> listener.locationFocusChanged(vertexId)); Swing.runLater(() -> listener.locationFocusChanged(vertex));
} }
@Override @Override
public void selectVertices(List<String> vertexIdList, EventTrigger eventTrigger) { public void selectVertices(Set<AttributedVertex> selected, EventTrigger eventTrigger) {
// if we are not to fire events, turn off the selection listener we provided to the // if we are not to fire events, turn off the selection listener we provided to the
// graphing library. // graphing library.
switchableSelectionListener.setEnabled(eventTrigger != EventTrigger.INTERNAL_ONLY); switchableSelectionListener.setEnabled(eventTrigger != EventTrigger.INTERNAL_ONLY);
@ -792,8 +791,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
try { try {
MutableSelectedState<AttributedVertex> nodeSelectedState = MutableSelectedState<AttributedVertex> nodeSelectedState =
viewer.getSelectedVertexState(); viewer.getSelectedVertexState();
Set<AttributedVertex> selected = getVertices(vertexIdList); if (selected.isEmpty()) {
if (vertexIdList.isEmpty()) {
nodeSelectedState.clear(); nodeSelectedState.clear();
} }
else if (!Arrays.asList(nodeSelectedState.getSelectedObjects()).containsAll(selected)) { else if (!Arrays.asList(nodeSelectedState.getSelectedObjects()).containsAll(selected)) {
@ -809,30 +807,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
} }
} }
/**
*
* @param vertexIds vertex ids of interest
* @return a {@code Set} containing the {@code AttributedVertex} for ths supplied ids
*/
private Set<AttributedVertex> getVertices(Collection<String> vertexIds) {
Set<String> vertexSet = new HashSet<>(vertexIds);
return graph.vertexSet()
.stream()
.filter(v -> vertexSet.contains(v.getId()))
.collect(Collectors.toSet());
}
@Override
public void setLocationFocus(String vertexID, EventTrigger eventTrigger) {
Optional<AttributedVertex> vertexToFocus =
graph.vertexSet().stream().filter(v -> vertexID.equals(v.getId())).findFirst();
log.fine("picking address:" + vertexID + " returned " + vertexToFocus);
viewer.repaint();
vertexToFocus.ifPresent(v -> {
setFocusedVertex(v, eventTrigger);
});
viewer.repaint();
}
/** /**
* set the {@link AttributedGraph} for visualization * set the {@link AttributedGraph} for visualization
@ -1066,23 +1040,15 @@ public class DefaultGraphDisplay implements GraphDisplay {
/** /**
* process a request to update the name attribute value of the vertex with the * process a request to update the name attribute value of the vertex with the
* supplied id * supplied id
* @param id the vertix id * @param vertex the vertex to update
* @param newName the new name of the vertex * @param newName the new name of the vertex
*/ */
@Override @Override
public void updateVertexName(String id, String newName) { public void updateVertexName(AttributedVertex vertex, String newName) {
// find the vertex, if present, change the name vertex.setName(newName);
Optional<AttributedVertex> optional = graph.vertexSet() vertex.clearCache();
.stream() iconCache.evict(vertex);
.filter(v -> v.getId().equals(id)) viewer.repaint();
.findFirst();
if (optional.isPresent()) {
AttributedVertex vertex = optional.get();
vertex.setName(newName);
vertex.clearCache();
iconCache.evict(vertex);
viewer.repaint();
}
} }
/** /**
@ -1225,8 +1191,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
// vertices // vertices
if (e.getStateChange() == ItemEvent.SELECTED) { if (e.getStateChange() == ItemEvent.SELECTED) {
Collection<AttributedVertex> selectedVertices = getVertices(e.getItem()); Collection<AttributedVertex> selectedVertices = getVertices(e.getItem());
List<String> selectedVertexIds = toVertexIds(selectedVertices); notifySelectionChanged(new HashSet<AttributedVertex>(selectedVertices));
notifySelectionChanged(selectedVertexIds);
if (selectedVertices.size() == 1) { if (selectedVertices.size() == 1) {
// if only one vertex was selected, make it the focused vertex // if only one vertex was selected, make it the focused vertex
@ -1239,7 +1204,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
} }
} }
else if (e.getStateChange() == ItemEvent.DESELECTED) { else if (e.getStateChange() == ItemEvent.DESELECTED) {
notifySelectionChanged(Collections.emptyList()); notifySelectionChanged(Collections.emptySet());
} }
viewer.repaint(); viewer.repaint();
} }
@ -1255,14 +1220,13 @@ public class DefaultGraphDisplay implements GraphDisplay {
} }
@Override @Override
public String getFocusedVertexId() { public AttributedVertex getFocusedVertex() {
return focusedVertex == null ? null : focusedVertex.getId(); return focusedVertex;
} }
@Override @Override
public Set<String> getSelectedVertexIds() { public Set<AttributedVertex> getSelectedVertices() {
Set<AttributedVertex> selectedVertices = getSelectedVertices(); return viewer.getSelectedVertexState().getSelected();
return selectedVertices.stream().map(v -> v.getId()).collect(Collectors.toSet());
} }
public ActionContext getActionContext(MouseEvent e) { public ActionContext getActionContext(MouseEvent e) {
@ -1284,9 +1248,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
} }
private Set<AttributedVertex> getSelectedVertices() {
return viewer.getSelectedVertexState().getSelected();
}
/** /**
* Use the hide selected action states to determine what vertices are shown: * Use the hide selected action states to determine what vertices are shown:
@ -1321,4 +1282,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
viewer.repaint(); viewer.repaint();
} }
@Override
public AttributedGraph getGraph() {
return graph;
}
} }

View file

@ -157,13 +157,13 @@ public class BlockGraphTask extends Task {
if (location != null) { if (location != null) {
// initialize the graph location, but don't have the graph send an event // initialize the graph location, but don't have the graph send an event
String id = listener.getVertexIdForAddress(location.getAddress()); AttributedVertex vertex = listener.getVertex(location.getAddress());
display.setLocationFocus(id, EventTrigger.INTERNAL_ONLY); display.setFocusedVertex(vertex, EventTrigger.INTERNAL_ONLY);
} }
if (selection != null && !selection.isEmpty()) { if (selection != null && !selection.isEmpty()) {
List<String> selectedVertices = listener.getVertices(selection); Set<AttributedVertex> selectedVertices = listener.getVertices(selection);
if (selectedVertices != null) { if (selectedVertices != null) {
// intialize the graph selection, but don't have the graph send an event // initialize the graph selection, but don't have the graph send an event
display.selectVertices(selectedVertices, EventTrigger.INTERNAL_ONLY); display.selectVertices(selectedVertices, EventTrigger.INTERNAL_ONLY);
} }
} }

View file

@ -54,28 +54,28 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
} }
@Override @Override
protected String getVertexIdForAddress(Address address) { protected String getVertexId(Address address) {
try { try {
CodeBlock[] blocks = blockModel.getCodeBlocksContaining(address, TaskMonitor.DUMMY); CodeBlock[] blocks = blockModel.getCodeBlocksContaining(address, TaskMonitor.DUMMY);
if (blocks != null && blocks.length > 0) { if (blocks != null && blocks.length > 0) {
return super.getVertexIdForAddress(blocks[0].getFirstStartAddress()); return super.getVertexId(blocks[0].getFirstStartAddress());
} }
} }
catch (CancelledException e) { catch (CancelledException e) {
// Will not happen with dummyMonitor // Will not happen with dummyMonitor
// Model has already done the work when the graph was created // Model has already done the work when the graph was created
} }
return super.getVertexIdForAddress(address); return super.getVertexId(address);
} }
@Override @Override
protected List<String> getVertices(AddressSetView addrSet) { protected Set<AttributedVertex> getVertices(AddressSetView addrSet) {
if (addrSet.isEmpty()) { if (addrSet.isEmpty()) {
return Collections.emptyList(); return Collections.emptySet();
} }
// Identify all blocks which have an entry point within the selection address set // Identify all blocks which have an entry point within the selection address set
ArrayList<String> blockList = new ArrayList<String>(); Set<AttributedVertex> vertices = new HashSet<>();
try { try {
SymbolTable symTable = program.getSymbolTable(); SymbolTable symTable = program.getSymbolTable();
CodeBlockIterator cbIter = CodeBlockIterator cbIter =
@ -91,7 +91,10 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
else { else {
addrString = addr.toString(); addrString = addr.toString();
} }
blockList.add(addrString); AttributedVertex vertex = graphDisplay.getGraph().getVertex(addrString);
if (vertex != null) {
vertices.add(vertex);
}
} }
} }
catch (CancelledException e) { catch (CancelledException e) {
@ -99,18 +102,18 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
// Model has already done the work when the graph was created // Model has already done the work when the graph was created
} }
return blockList; return vertices;
} }
@Override @Override
protected AddressSet getAddressSetForVertices(List<String> vertexIds) { protected AddressSet getAddresses(Set<AttributedVertex> vertices) {
AddressSet addrSet = new AddressSet(); AddressSet addrSet = new AddressSet();
try { try {
// for each address string, translate it into a block // for each address string, translate it into a block
// and add it to the address set. // and add it to the address set.
for (String vertexId : vertexIds) { for (AttributedVertex vertex : vertices) {
Address blockAddr = getAddressForVertexId(vertexId); Address blockAddr = getAddress(vertex);
if (!isValidAddress(blockAddr)) { if (!isValidAddress(blockAddr)) {
continue; continue;
} }
@ -150,8 +153,8 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
} }
private void updateVertexName(VertexGraphActionContext context) { private void updateVertexName(VertexGraphActionContext context) {
String vertexId = context.getClickedVertex().getId(); AttributedVertex vertex = context.getClickedVertex();
Address address = getAddressForVertexId(vertexId); Address address = getAddress(vertex);
Symbol symbol = program.getSymbolTable().getPrimarySymbol(address); Symbol symbol = program.getSymbolTable().getPrimarySymbol(address);
if (symbol == null) { if (symbol == null) {
@ -165,8 +168,8 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
} }
@Override @Override
public GraphDisplayListener cloneWith(GraphDisplay graphDisplay) { public GraphDisplayListener cloneWith(GraphDisplay newGraphDisplay) {
return new BlockModelGraphDisplayListener(tool, blockModel, graphDisplay); return new BlockModelGraphDisplayListener(tool, blockModel, newGraphDisplay);
} }
} }

View file

@ -17,7 +17,8 @@ package ghidra.graph.program;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.*; import java.util.HashSet;
import java.util.Set;
import org.junit.Test; import org.junit.Test;
@ -27,6 +28,7 @@ import ghidra.program.model.block.CodeBlockModel;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.service.graph.AttributedGraph; import ghidra.service.graph.AttributedGraph;
import ghidra.service.graph.AttributedVertex;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class BlockGraphEventTest extends AbstractBlockGraphTest { public class BlockGraphEventTest extends AbstractBlockGraphTest {
@ -54,13 +56,13 @@ public class BlockGraphEventTest extends AbstractBlockGraphTest {
@Test @Test
public void testGhidraLocationChanged() { public void testGhidraLocationChanged() {
codeBrowser.goTo(new ProgramLocation(program, addr(0x1002239))); codeBrowser.goTo(new ProgramLocation(program, addr(0x1002239)));
assertEquals("01002239", display.getFocusedVertex()); assertEquals("01002239", display.getFocusedVertex().getId());
codeBrowser.goTo(new ProgramLocation(program, addr(0x1002200))); codeBrowser.goTo(new ProgramLocation(program, addr(0x1002200)));
assertEquals("01002200", display.getFocusedVertex()); assertEquals("01002200", display.getFocusedVertex().getId());
// also try a location that is not the start of a block // also try a location that is not the start of a block
codeBrowser.goTo(new ProgramLocation(program, addr(0x100223a))); codeBrowser.goTo(new ProgramLocation(program, addr(0x100223a)));
assertEquals("01002239", display.getFocusedVertex()); assertEquals("01002239", display.getFocusedVertex().getId());
} }
@ -71,38 +73,38 @@ public class BlockGraphEventTest extends AbstractBlockGraphTest {
@Test @Test
public void testGhidraSelectionChanged() { public void testGhidraSelectionChanged() {
setSelection(addrSet(0x1002239, 0x1002241)); setSelection(addrSet(0x1002239, 0x1002241));
Set<String> selected = new HashSet<>(display.getSelectedVertices()); Set<AttributedVertex> selected = new HashSet<>(display.getSelectedVertices());
assertEquals(3, selected.size()); assertEquals(3, selected.size());
assertTrue(selected.contains("01002239")); assertTrue(selected.contains(graph.getVertex("01002239")));
assertTrue(selected.contains("0100223c")); assertTrue(selected.contains(graph.getVertex("0100223c")));
assertTrue(selected.contains("0100223e")); assertTrue(selected.contains(graph.getVertex("0100223e")));
setSelection(new AddressSet(addr(0x1002200), addr(0x1002210))); setSelection(new AddressSet(addr(0x1002200), addr(0x1002210)));
selected = new HashSet<>(display.getSelectedVertices()); selected = new HashSet<>(display.getSelectedVertices());
assertEquals(2, selected.size()); assertEquals(2, selected.size());
assertTrue(selected.contains("01002200")); assertTrue(selected.contains(graph.getVertex("01002200")));
assertTrue(selected.contains("01002203")); assertTrue(selected.contains(graph.getVertex("01002203")));
} }
@Test @Test
public void testGraphNodeFocused() { public void testGraphNodeFocused() {
display.focusChanged("01002203"); display.focusChanged(graph.getVertex("01002203"));
assertEquals(addr(0x01002203), codeBrowser.getCurrentLocation().getAddress()); assertEquals(addr(0x01002203), codeBrowser.getCurrentLocation().getAddress());
display.focusChanged("0100223c"); display.focusChanged(graph.getVertex("0100223c"));
assertEquals(addr(0x0100223c), codeBrowser.getCurrentLocation().getAddress()); assertEquals(addr(0x0100223c), codeBrowser.getCurrentLocation().getAddress());
} }
@Test @Test
public void testGraphNodesSelected() { public void testGraphNodesSelected() {
display.selectionChanged(Arrays.asList("01002239", "0100223c")); display.selectionChanged(Set.of(graph.getVertex("01002239"), graph.getVertex("0100223c")));
ProgramSelection selection = codeBrowser.getCurrentSelection(); ProgramSelection selection = codeBrowser.getCurrentSelection();
assertEquals(addr(0x01002239), selection.getMinAddress()); assertEquals(addr(0x01002239), selection.getMinAddress());
assertEquals(addr(0x0100223d), selection.getMaxAddress()); assertEquals(addr(0x0100223d), selection.getMaxAddress());
display.selectionChanged(Arrays.asList("01002200", "01002203")); display.selectionChanged(Set.of(graph.getVertex("01002200"), graph.getVertex("01002203")));
selection = codeBrowser.getCurrentSelection(); selection = codeBrowser.getCurrentSelection();
assertEquals(addr(0x01002200), selection.getMinAddress()); assertEquals(addr(0x01002200), selection.getMinAddress());
assertEquals(addr(0x01002204), selection.getMaxAddress()); assertEquals(addr(0x01002204), selection.getMaxAddress());

View file

@ -15,7 +15,8 @@
*/ */
package ghidra.graph.program; package ghidra.graph.program;
import java.util.*; import java.util.HashSet;
import java.util.Set;
import docking.action.DockingAction; import docking.action.DockingAction;
import docking.widgets.EventTrigger; import docking.widgets.EventTrigger;
@ -26,12 +27,11 @@ import ghidra.util.task.TaskMonitor;
public class TestGraphDisplay implements GraphDisplay { public class TestGraphDisplay implements GraphDisplay {
private Set<String> definedVertexAttributes = new HashSet<>(); private Set<String> definedVertexAttributes = new HashSet<>();
private Set<String> definedEdgeAttributes = new HashSet<>(); private Set<String> definedEdgeAttributes = new HashSet<>();
private String vertexAttributeName;
private AttributedGraph graph; private AttributedGraph graph;
private String graphDescription; private String graphDescription;
private GraphDisplayListener listener; private GraphDisplayListener listener;
private String currentFocusedVertex; private AttributedVertex focusedVertex;
private List<String> currentSelection; private Set<AttributedVertex> currentSelection;
@Override @Override
public void setGraphDisplayListener(GraphDisplayListener listener) { public void setGraphDisplayListener(GraphDisplayListener listener) {
@ -39,20 +39,22 @@ public class TestGraphDisplay implements GraphDisplay {
} }
@Override @Override
public void setLocationFocus(String vertexID, EventTrigger eventTrigger) { public void setFocusedVertex(AttributedVertex vertex, EventTrigger eventTrigger) {
currentFocusedVertex = vertexID; focusedVertex = vertex;
}
public String getFocusedVertex() {
return currentFocusedVertex;
} }
@Override @Override
public void selectVertices(List<String> vertexList, EventTrigger eventTrigger) { public AttributedVertex getFocusedVertex() {
return focusedVertex;
}
@Override
public void selectVertices(Set<AttributedVertex> vertexList, EventTrigger eventTrigger) {
currentSelection = vertexList; currentSelection = vertexList;
} }
public List<String> getSelectedVertices() { @Override
public Set<AttributedVertex> getSelectedVertices() {
return currentSelection; return currentSelection;
} }
@ -74,7 +76,7 @@ public class TestGraphDisplay implements GraphDisplay {
@Override @Override
public void setVertexLabel(String attributeName, int alignment, int size, boolean monospace, public void setVertexLabel(String attributeName, int alignment, int size, boolean monospace,
int maxLines) { int maxLines) {
vertexAttributeName = attributeName; // nothing
} }
@Override @Override
@ -91,7 +93,7 @@ public class TestGraphDisplay implements GraphDisplay {
} }
@Override @Override
public void updateVertexName(String id, String newName) { public void updateVertexName(AttributedVertex vertex, String newName) {
// nothing // nothing
} }
@ -100,16 +102,17 @@ public class TestGraphDisplay implements GraphDisplay {
return graphDescription; return graphDescription;
} }
@Override
public AttributedGraph getGraph() { public AttributedGraph getGraph() {
return graph; return graph;
} }
public void focusChanged(String vertexId) { public void focusChanged(AttributedVertex vertex) {
listener.locationFocusChanged(vertexId); listener.locationFocusChanged(vertex);
} }
public void selectionChanged(List<String> vertexIds) { public void selectionChanged(Set<AttributedVertex> vertices) {
listener.selectionChanged(vertexIds); listener.selectionChanged(vertices);
} }
@Override @Override
@ -117,14 +120,4 @@ public class TestGraphDisplay implements GraphDisplay {
// do nothing, actions are not supported by this display // do nothing, actions are not supported by this display
} }
@Override
public String getFocusedVertexId() {
return currentFocusedVertex;
}
@Override
public Set<String> getSelectedVertexIds() {
return new HashSet<String>(currentSelection);
}
} }

View file

@ -15,7 +15,7 @@
*/ */
package ghidra.service.graph; package ghidra.service.graph;
import java.util.List; import java.util.Set;
public class DummyGraphDisplayListener implements GraphDisplayListener { public class DummyGraphDisplayListener implements GraphDisplayListener {
@ -24,19 +24,19 @@ public class DummyGraphDisplayListener implements GraphDisplayListener {
// I'm a dummy // I'm a dummy
} }
@Override
public void selectionChanged(List<String> vertexIds) {
// I'm a dummy
}
@Override
public void locationFocusChanged(String vertexId) {
// I'm a dummy
}
@Override @Override
public GraphDisplayListener cloneWith(GraphDisplay graphDisplay) { public GraphDisplayListener cloneWith(GraphDisplay graphDisplay) {
return new DummyGraphDisplayListener(); return new DummyGraphDisplayListener();
} }
@Override
public void selectionChanged(Set<AttributedVertex> vertices) {
// I'm a dummy
}
@Override
public void locationFocusChanged(AttributedVertex vertex) {
// I'm a dummy
}
} }

View file

@ -15,7 +15,6 @@
*/ */
package ghidra.service.graph; package ghidra.service.graph;
import java.util.List;
import java.util.Set; import java.util.Set;
import docking.action.DockingAction; import docking.action.DockingAction;
@ -46,41 +45,46 @@ public interface GraphDisplay {
/** /**
* Tells the graph display window to focus the vertex with the given id. * Tells the graph display window to focus the vertex with the given id.
* *
* @param vertexID the id of the vertex to focus * @param vertex the vertex to focus
* @param eventTrigger Provides a hint to the GraphDisplay as to why we are updating the * @param eventTrigger Provides a hint to the GraphDisplay as to why we are updating the
* graph location so that the GraphDisplay can decide if it should send out a notification via * graph location so that the GraphDisplay can decide if it should send out a notification via
* the {@link GraphDisplayListener#locationFocusChanged(String)}. For example, if we are updating * the {@link GraphDisplayListener#locationFocusChanged(AttributedVertex)}. For example, if we
* the the location due to an event from the main application, we don't want to notify the * are updating the the location due to an event from the main application, we don't want to
* application the graph changed to avoid event cycles. See {@link EventTrigger} for more * notify the application the graph changed to avoid event cycles. See {@link EventTrigger} for
* information. * more information.
*
*/ */
public void setLocationFocus(String vertexID, EventTrigger eventTrigger); public void setFocusedVertex(AttributedVertex vertex, EventTrigger eventTrigger);
/** /**
* Returns the currently focused vertexID or null if no vertex is focussed. * Returns the graph for this display
* @return the currently focused vertexID or null if no vertex is focussed. * @return the graph for this display
*/ */
public String getFocusedVertexId(); public AttributedGraph getGraph();
/**
* Returns the currently focused vertex or null if no vertex is focused
* @return the currently focused vertex or null if no vertex is focused.
*/
public AttributedVertex getFocusedVertex();
/** /**
* Tells the graph display window to select the vertices with the given ids * Tells the graph display window to select the vertices with the given ids
* *
* @param vertexList the list of vertex ids to select * @param vertexSet the set of vertices to select
* @param eventTrigger Provides a hint to the GraphDisplay as to why we are updating the * @param eventTrigger Provides a hint to the GraphDisplay as to why we are updating the
* graph location so that the GraphDisplay can decide if it should send out a notification via * graph location so that the GraphDisplay can decide if it should send out a notification via
* the {@link GraphDisplayListener#locationFocusChanged(String)}. For example, if we are updating * the {@link GraphDisplayListener#selectionChanged(Set)}. For example, if we are updating
* the the location due to an event from the main application, we don't want to notify the * the the location due to an event from the main application, we don't want to notify the
* application the graph changed to avoid event cycles. See {@link EventTrigger} for more * application the graph changed to avoid event cycles. See {@link EventTrigger} for more
* information. * information.
*/ */
public void selectVertices(List<String> vertexList, EventTrigger eventTrigger); public void selectVertices(Set<AttributedVertex> vertexSet, EventTrigger eventTrigger);
/** /**
* Returns a list of vertex ids for all the currently selected vertices * Returns a set of vertex ids for all the currently selected vertices
* @return a list of vertex ids for all the currently selected vertices * @return a set of vertex ids for all the currently selected vertices
*/ */
public Set<String> getSelectedVertexIds(); public Set<AttributedVertex> getSelectedVertices();
/** /**
* Closes this graph display window. * Closes this graph display window.
@ -131,10 +135,10 @@ public interface GraphDisplay {
/** /**
* Updates a vertex to a new name * Updates a vertex to a new name
* @param id the vertex id * @param vertex the vertex to rename
* @param newName the new name of the vertex * @param newName the new name for the vertex
*/ */
public void updateVertexName(String id, String newName); public void updateVertexName(AttributedVertex vertex, String newName);
/** /**
* Returns the description of the current graph * Returns the description of the current graph
@ -148,4 +152,5 @@ public interface GraphDisplay {
* @param action the action to add. * @param action the action to add.
*/ */
public void addAction(DockingAction action); public void addAction(DockingAction action);
} }

View file

@ -15,7 +15,7 @@
*/ */
package ghidra.service.graph; package ghidra.service.graph;
import java.util.List; import java.util.Set;
/** /**
* Interface for being notified when the user interacts with a visual graph display * Interface for being notified when the user interacts with a visual graph display
@ -27,17 +27,17 @@ public interface GraphDisplayListener {
public void graphClosed(); public void graphClosed();
/** /**
* Notification that the list of selected vertices has changed * Notification that the set of selected vertices has changed
* *
* @param vertexIds the list of vertex ids for the currently selected vertices * @param vertices the set of currently selected vertices
*/ */
public void selectionChanged(List<String> vertexIds); public void selectionChanged(Set<AttributedVertex> vertices);
/** /**
* Notification that the "focused" (active) vertex has changed * Notification that the "focused" (active) vertex has changed
* @param vertexId the vertex id of the currently "focused" vertex * @param vertex the vertex that is currently "focused"
*/ */
public void locationFocusChanged(String vertexId); public void locationFocusChanged(AttributedVertex vertex);
/** /**
* Makes a new GraphDisplayListener of the same type as the specific * Makes a new GraphDisplayListener of the same type as the specific

View file

@ -40,6 +40,12 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
private AttributedGraph graph; private AttributedGraph graph;
private ComponentProvider graphComponentProvider; private ComponentProvider graphComponentProvider;
private GraphDisplay display; private GraphDisplay display;
private AttributedVertex a;
private AttributedVertex b;
private AttributedVertex c;
private AttributedVertex d;
private AttributedVertex e;
private AttributedVertex f;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@ -60,7 +66,7 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
@Test @Test
public void testSelectVertexAction() { public void testSelectVertexAction() {
assertTrue(display.getSelectedVertexIds().isEmpty()); assertTrue(display.getSelectedVertices().isEmpty());
DockingActionIf action = getAction(tool, "Select Vertex"); DockingActionIf action = getAction(tool, "Select Vertex");
VertexGraphActionContext context = VertexGraphActionContext context =
@ -68,25 +74,24 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
graph.getVertex("B")); graph.getVertex("B"));
performAction(action, context, true); performAction(action, context, true);
Set<String> selectedVertexIds = display.getSelectedVertexIds(); Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
assertEquals(1, selectedVertexIds.size()); assertEquals(1, selectedVertices.size());
assertTrue(selectedVertexIds.contains(graph.getVertex("B").getId())); assertTrue(selectedVertices.contains(b));
// now try and select a second vertex // now try and select a second vertex
context = new VertexGraphActionContext(graphComponentProvider, graph, null, null, context = new VertexGraphActionContext(graphComponentProvider, graph, null, null,d);
graph.getVertex("D"));
performAction(action, context, true); performAction(action, context, true);
selectedVertexIds = display.getSelectedVertexIds(); selectedVertices = display.getSelectedVertices();
assertEquals(2, selectedVertexIds.size()); assertEquals(2, selectedVertices.size());
assertTrue(selectedVertexIds.contains(graph.getVertex("B").getId())); assertTrue(selectedVertices.contains(b));
assertTrue(selectedVertexIds.contains(graph.getVertex("D").getId())); assertTrue(selectedVertices.contains(d));
} }
@Test @Test
public void testDeSelectVertexAction() { public void testDeSelectVertexAction() {
display.selectVertices(Arrays.asList("A", "B", "C", "D"), EventTrigger.API_CALL); select(a, b, c, d);
assertEquals(4, display.getSelectedVertexIds().size()); assertEquals(4, display.getSelectedVertices().size());
DockingActionIf action = getAction(tool, "Deselect Vertex"); DockingActionIf action = getAction(tool, "Deselect Vertex");
VertexGraphActionContext context = VertexGraphActionContext context =
@ -94,18 +99,18 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
graph.getVertex("B")); graph.getVertex("B"));
performAction(action, context, true); performAction(action, context, true);
Set<String> selectedVerticeIds = display.getSelectedVertexIds(); Set<AttributedVertex> selected = display.getSelectedVertices();
assertEquals(3, selectedVerticeIds.size()); assertEquals(3, selected.size());
assertTrue(selectedVerticeIds.contains(graph.getVertex("A").getId())); assertTrue(selected.contains(a));
assertTrue(selectedVerticeIds.contains(graph.getVertex("D").getId())); assertTrue(selected.contains(c));
assertTrue(selectedVerticeIds.contains(graph.getVertex("D").getId())); assertTrue(selected.contains(d));
assertFalse(selectedVerticeIds.contains(graph.getVertex("B").getId())); assertFalse(selected.contains(b));
} }
@Test @Test
public void testSelectEdgeAction() { public void testSelectEdgeAction() {
assertTrue(display.getSelectedVertexIds().isEmpty()); assertTrue(display.getSelectedVertices().isEmpty());
DockingActionIf action = getAction(tool, "Select Edge"); DockingActionIf action = getAction(tool, "Select Edge");
EdgeGraphActionContext context = EdgeGraphActionContext context =
@ -113,10 +118,10 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
graph.getEdge(graph.getVertex("A"), graph.getVertex("B"))); graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
performAction(action, context, true); performAction(action, context, true);
Set<String> selectedVerticeIds = display.getSelectedVertexIds(); Set<AttributedVertex> selectedVerticeIds = display.getSelectedVertices();
assertEquals(2, selectedVerticeIds.size()); assertEquals(2, selectedVerticeIds.size());
assertTrue(selectedVerticeIds.contains(graph.getVertex("A").getId())); assertTrue(selectedVerticeIds.contains(a));
assertTrue(selectedVerticeIds.contains(graph.getVertex("B").getId())); assertTrue(selectedVerticeIds.contains(b));
} }
@Test @Test
@ -127,85 +132,85 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
graph.getEdge(graph.getVertex("A"), graph.getVertex("B"))); graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
performAction(action, context, true); performAction(action, context, true);
Set<String> selectedVerticeIds = display.getSelectedVertexIds(); Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
assertEquals(2, selectedVerticeIds.size()); assertEquals(2, selectedVertices.size());
action = getAction(tool, "Deselect Edge"); action = getAction(tool, "Deselect Edge");
performAction(action, context, true); performAction(action, context, true);
selectedVerticeIds = display.getSelectedVertexIds(); selectedVertices = display.getSelectedVertices();
assertEquals(0, selectedVerticeIds.size()); assertEquals(0, selectedVertices.size());
} }
@Test @Test
public void testSelectEdgeSource() { public void testSelectEdgeSource() {
display.setLocationFocus("D", EventTrigger.INTERNAL_ONLY); setFocusedVertex(d);
DockingActionIf action = getAction(tool, "Edge Source"); DockingActionIf action = getAction(tool, "Edge Source");
EdgeGraphActionContext context = EdgeGraphActionContext context =
new EdgeGraphActionContext(graphComponentProvider, graph, null, null, new EdgeGraphActionContext(graphComponentProvider, graph, null, null,
graph.getEdge(graph.getVertex("A"), graph.getVertex("B"))); graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
performAction(action, context, true); performAction(action, context, true);
assertEquals("A", display.getFocusedVertexId()); assertEquals(a, display.getFocusedVertex());
} }
@Test @Test
public void testSelectEdgeTarget() { public void testSelectEdgeTarget() {
display.setLocationFocus("D", EventTrigger.INTERNAL_ONLY); setFocusedVertex(d);
DockingActionIf action = getAction(tool, "Edge Target"); DockingActionIf action = getAction(tool, "Edge Target");
EdgeGraphActionContext context = EdgeGraphActionContext context =
new EdgeGraphActionContext(graphComponentProvider, graph, null, null, new EdgeGraphActionContext(graphComponentProvider, graph, null, null,
graph.getEdge(graph.getVertex("A"), graph.getVertex("B"))); graph.getEdge(a, b));
performAction(action, context, true); performAction(action, context, true);
assertEquals("B", display.getFocusedVertexId()); assertEquals(b, display.getFocusedVertex());
} }
@Test @Test
public void testInvertSelection() { public void testInvertSelection() {
display.selectVertices(List.of("A", "C", "E"), EventTrigger.INTERNAL_ONLY); select(a, c, e);
DockingActionIf action = getAction(tool, "Invert Selection"); DockingActionIf action = getAction(tool, "Invert Selection");
GraphActionContext context = GraphActionContext context =
new GraphActionContext(graphComponentProvider, graph, null, null); new GraphActionContext(graphComponentProvider, graph, null, null);
performAction(action, context, true); performAction(action, context, true);
Set<String> selectedVerticeIds = display.getSelectedVertexIds(); Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
assertEquals(3, selectedVerticeIds.size()); assertEquals(3, selectedVertices.size());
assertTrue(selectedVerticeIds.contains("B")); assertTrue(selectedVertices.contains(b));
assertTrue(selectedVerticeIds.contains("D")); assertTrue(selectedVertices.contains(d));
assertTrue(selectedVerticeIds.contains("F")); assertTrue(selectedVertices.contains(f));
} }
@Test @Test
public void testGrowSelectionOut() { public void testGrowSelectionOut() {
display.selectVertices(List.of("A"), EventTrigger.INTERNAL_ONLY); select(a);
DockingActionIf action = getAction(tool, "Grow Selection To Targets"); DockingActionIf action = getAction(tool, "Grow Selection To Targets");
GraphActionContext context = GraphActionContext context =
new GraphActionContext(graphComponentProvider, graph, null, null); new GraphActionContext(graphComponentProvider, graph, null, null);
performAction(action, context, true); performAction(action, context, true);
Set<String> selectedVerticeIds = display.getSelectedVertexIds(); Set<AttributedVertex> selectedVerticeIds = display.getSelectedVertices();
assertEquals(3, selectedVerticeIds.size()); assertEquals(3, selectedVerticeIds.size());
assertTrue(selectedVerticeIds.contains("A")); assertTrue(selectedVerticeIds.contains(a));
assertTrue(selectedVerticeIds.contains("B")); assertTrue(selectedVerticeIds.contains(b));
assertTrue(selectedVerticeIds.contains("C")); assertTrue(selectedVerticeIds.contains(c));
} }
@Test @Test
public void testGrowSelectionIn() { public void testGrowSelectionIn() {
display.selectVertices(List.of("D"), EventTrigger.INTERNAL_ONLY); select(d);
DockingActionIf action = getAction(tool, "Grow Selection From Sources"); DockingActionIf action = getAction(tool, "Grow Selection From Sources");
GraphActionContext context = GraphActionContext context =
new GraphActionContext(graphComponentProvider, graph, null, null); new GraphActionContext(graphComponentProvider, graph, null, null);
performAction(action, context, true); performAction(action, context, true);
Set<String> selectedVerticeIds = display.getSelectedVertexIds(); Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
assertEquals(3, selectedVerticeIds.size()); assertEquals(3, selectedVertices.size());
assertTrue(selectedVerticeIds.contains("D")); assertTrue(selectedVertices.contains(d));
assertTrue(selectedVerticeIds.contains("B")); assertTrue(selectedVertices.contains(b));
assertTrue(selectedVerticeIds.contains("C")); assertTrue(selectedVertices.contains(c));
} }
@Test @Test
@ -216,7 +221,7 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(1, graphProviders.size()); assertEquals(1, graphProviders.size());
DefaultGraphDisplayComponentProvider original = graphProviders.get(0); DefaultGraphDisplayComponentProvider original = graphProviders.get(0);
display.selectVertices(List.of("B", "C", "D"), EventTrigger.INTERNAL_ONLY); select(b, c, d);
DockingActionIf action = getAction(tool, "Create Subgraph"); DockingActionIf action = getAction(tool, "Create Subgraph");
GraphActionContext context = GraphActionContext context =
new GraphActionContext(graphComponentProvider, graph, null, null); new GraphActionContext(graphComponentProvider, graph, null, null);
@ -241,8 +246,8 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
assertFalse(contains(newGraph, "F")); assertFalse(contains(newGraph, "F"));
} }
private boolean contains(AttributedGraph graph, String vertexId) { private boolean contains(AttributedGraph g, String vertexId) {
return graph.getVertex(vertexId) != null; return g.getVertex(vertexId) != null;
} }
private void showGraph() throws Exception { private void showGraph() throws Exception {
@ -253,6 +258,17 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
display.setGraphDisplayListener(new TestGraphDisplayListener("test")); display.setGraphDisplayListener(new TestGraphDisplayListener("test"));
} }
private void select(AttributedVertex... vertices) {
runSwing(() -> {
Set<AttributedVertex> vetexSet = new HashSet<>(Arrays.asList(vertices));
display.selectVertices(vetexSet, EventTrigger.INTERNAL_ONLY);
});
}
private void setFocusedVertex(AttributedVertex vertex) {
runSwing(() -> display.setFocusedVertex(vertex, EventTrigger.INTERNAL_ONLY));
}
class TestGraphDisplayListener implements GraphDisplayListener { class TestGraphDisplayListener implements GraphDisplayListener {
private String name; private String name;
@ -267,20 +283,20 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
} }
@Override @Override
public void selectionChanged(List<String> vertexIds) { public void selectionChanged(Set<AttributedVertex> verrtices) {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
buf.append(name); buf.append(name);
buf.append(": selected: "); buf.append(": selected: ");
for (String id : vertexIds) { for (AttributedVertex vertex : verrtices) {
buf.append(id); buf.append(vertex.getId());
buf.append(","); buf.append(",");
} }
listenerCalls.add(buf.toString()); listenerCalls.add(buf.toString());
} }
@Override @Override
public void locationFocusChanged(String vertexId) { public void locationFocusChanged(AttributedVertex vertex) {
listenerCalls.add(name + ": focus: " + vertexId); listenerCalls.add(name + ": focus: " + vertex.getId());
} }
@Override @Override
@ -292,12 +308,12 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
private AttributedGraph createGraph() { private AttributedGraph createGraph() {
AttributedGraph g = new AttributedGraph(); AttributedGraph g = new AttributedGraph();
AttributedVertex a = g.addVertex("A"); a = g.addVertex("A");
AttributedVertex b = g.addVertex("B"); b = g.addVertex("B");
AttributedVertex c = g.addVertex("C"); c = g.addVertex("C");
AttributedVertex d = g.addVertex("D"); d = g.addVertex("D");
AttributedVertex e = g.addVertex("E"); e = g.addVertex("E");
AttributedVertex f = g.addVertex("F"); f = g.addVertex("F");
g.addEdge(a, b); g.addEdge(a, b);
g.addEdge(a, c); g.addEdge(a, c);