Merge remote-tracking branch 'origin/GP-773_ghidravore_graph_visualization_options--SQUASHED'

This commit is contained in:
Ryan Kurtz 2021-08-09 14:13:19 -04:00
commit 69e8119211
84 changed files with 4102 additions and 1822 deletions

View file

@ -16,17 +16,8 @@
package ghidra.service.graph;
import java.util.*;
import java.util.Map.Entry;
import org.apache.commons.text.StringEscapeUtils;
import com.google.common.base.Splitter;
public class Attributed {
/**
* cache of the html rendering of the vertex attributes
*/
private String htmlString;
private static final String DESCRIPTION = "Description";
/**
@ -39,7 +30,7 @@ public class Attributed {
* @return an unmodifiable view of the attribute map
*/
public Map<String, String> getAttributeMap() {
public Map<String, String> getAttributes() {
return Collections.unmodifiableMap(attributes);
}
@ -51,7 +42,6 @@ public class Attributed {
* @return the previous value of the attribute
*/
public String setAttribute(String key, String value) {
htmlString = null;
return attributes.put(key, value);
}
@ -154,7 +144,6 @@ public class Attributed {
* @return the previously set description
*/
public String setDescription(String value) {
htmlString = null;
return attributes.put(DESCRIPTION, value);
}
@ -167,35 +156,4 @@ public class Attributed {
return getAttribute(DESCRIPTION);
}
/**
* parse (one time) then cache the attributes to html
* @return the html string
*/
public String getHtmlString() {
if (htmlString != null) {
return htmlString;
}
htmlString = getDescription();
if (htmlString == null) { // if no description is set, create a default one
Set<Entry<String, String>> entries = entrySet();
if (entries.isEmpty()) {
return ""; // empty so tooltip clients can handle empty data
}
StringBuilder buf = new StringBuilder();
for (Map.Entry<String, String> entry : entries) {
buf.append(entry.getKey());
buf.append(":");
String value = entry.getValue();
value = StringEscapeUtils.escapeHtml4(value);
String split = String.join("<br>", Splitter.on('\n').split(value));
split = split.replaceAll("\\s", "&nbsp;");
buf.append(split);
buf.append("<br>");
}
htmlString = buf.toString();
}
return htmlString;
}
}

View file

@ -19,6 +19,7 @@ package ghidra.service.graph;
* Generic directed graph edge implementation
*/
public class AttributedEdge extends Attributed {
public static final String EDGE_TYPE_KEY = "EdgeType";
private final String id;
/**
@ -62,4 +63,21 @@ public class AttributedEdge extends Attributed {
return id.equals(other.id);
}
/**
* Returns the edge type for this edge
* @return the edge type for this edge
*/
public String getEdgeType() {
return getAttribute(EDGE_TYPE_KEY);
}
/**
* Sets the edge type for this edge. Should be a value defined by the {@link GraphType} for
* this graph, but there is no enforcement for this. If the value is not defined in GraphType,
* it will be rendered using the default edge color for {@link GraphType}
* @param edgeType the edge type for this edge
*/
public void setEdgeType(String edgeType) {
setAttribute(EDGE_TYPE_KEY, edgeType);
}
}

View file

@ -33,29 +33,80 @@ import org.jgrapht.graph.DefaultGraphType;
* to the same source/destination vertex pair.
*/
public class AttributedGraph extends AbstractBaseGraph<AttributedVertex, AttributedEdge> {
private static final String WEIGHT = "Weight";
public static final String WEIGHT = "Weight";
private Map<String, AttributedVertex> vertexMap = new HashMap<>();
private final boolean collapseDuplicateEdges;
private String name;
private GraphType type;
private String description;
/**
* Create a new empty AttributedGraph that automatically collapses duplicate edges
*
* @param name the name of the graph
* @param type the {@link GraphType} which defines valid vertex and edge types.
*/
public AttributedGraph() {
this(true);
public AttributedGraph(String name, GraphType type) {
this(name, type, name, true);
}
/**
* Create a new empty AttributedGraph that automatically collapses duplicate edges
*
* @param name the name of the graph
* @param type the {@link GraphType} which defines valid vertex and edge types.
* @param description a description of the graph
*/
public AttributedGraph(String name, GraphType type, String description) {
this(name, type, description, true);
}
/**
* Create a new empty AttributedGraph.
*
*
* @param name the name of the graph
* @param type the {@link GraphType} which defines valid vertex and edge types.
* @param description a description of the graph
* @param collapseDuplicateEdges if true, duplicate edges will be collapsed into a single
* edge with a "Weight" attribute whose value is the number of edges between those vertices.
*/
public AttributedGraph(boolean collapseDuplicateEdges) {
public AttributedGraph(String name, GraphType type, String description,
boolean collapseDuplicateEdges) {
super(new VertexSupplier(), new EdgeSupplier(), DefaultGraphType.directedPseudograph());
this.name = name;
this.type = type;
this.description = description;
this.collapseDuplicateEdges = collapseDuplicateEdges;
}
/**
* Returns the name of the graph
* @return the name of the graph
*/
public String getName() {
return name;
}
/**
* Returns a description of the graph
* @return a description of the graph
*/
public String getDescription() {
return description;
}
/**
* Returns the {@link GraphType} for this graph
* @return the {@link GraphType} for this graph
*/
public GraphType getGraphType() {
return type;
}
/**
* Adds a new vertex with the given id. The vertex's name will be the same as the id.
* If a vertex already exists with that id,

View file

@ -20,6 +20,8 @@ package ghidra.service.graph;
*/
public class AttributedVertex extends Attributed {
public static final String NAME_KEY = "Name";
public static final String VERTEX_TYPE_KEY = "VertexType";
private final String id;
/**
@ -43,7 +45,7 @@ public class AttributedVertex extends Attributed {
* @param name the new name for the vertex
*/
public void setName(String name) {
setAttribute("Name", name);
setAttribute(NAME_KEY, name);
}
/**
@ -60,7 +62,7 @@ public class AttributedVertex extends Attributed {
* @return the name of the vertex
*/
public String getName() {
return getAttribute("Name");
return getAttribute(NAME_KEY);
}
@Override
@ -88,4 +90,22 @@ public class AttributedVertex extends Attributed {
return id.equals(other.id);
}
/**
* Returns the vertex type for this vertex
* @return the vertex type for this vertex
*/
public String getVertexType() {
return getAttribute(VERTEX_TYPE_KEY);
}
/**
* Sets the vertex type for this vertex. Should be a value defined by the {@link GraphType} for
* this graph, but there is no enforcement for this. If the value is not defined in GraphType,
* it will be rendered using the default vertex shape and color for the {@link GraphType}
* @param vertexType the vertex type for this vertex
*/
public void setVertexType(String vertexType) {
setAttribute(VERTEX_TYPE_KEY, vertexType);
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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.service.graph;
/**
* Empty implementation of GraphDiaplayOptions. Used as an initial default to avoid null
* checks
*/
public class DefaultGraphDisplayOptions extends GraphDisplayOptions {
public DefaultGraphDisplayOptions() {
super(new EmptyGraphType());
}
}

View file

@ -0,0 +1,30 @@
/* ###
* 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.service.graph;
import java.util.Collections;
/**
* Default GraphType implementation that has no vertex or edge types defined
*/
public class EmptyGraphType extends GraphType {
public EmptyGraphType() {
super("Empty Graph Type", "Graph type with no defined vertex or edge types",
Collections.emptyList(), Collections.emptyList());
}
}

View file

@ -0,0 +1,831 @@
/* ###
* 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.service.graph;
import java.awt.Color;
import java.awt.Font;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.apache.commons.text.StringEscapeUtils;
import com.google.common.base.Splitter;
import docking.Tool;
import docking.options.editor.*;
import ghidra.framework.options.*;
import ghidra.util.HelpLocation;
import ghidra.util.WebColors;
import ghidra.util.bean.opteditor.OptionsVetoException;
/**
* Class for managing graph display options. This includes color options for each vertex
* and edge type and shapes for vertex types.
*/
public class GraphDisplayOptions implements OptionsChangeListener {
public static final GraphDisplayOptions DEFAULT =
new GraphDisplayOptions(new EmptyGraphType());
private static final String FONT = "Font";
private static final String LABEL_POSITION = "Label Position";
private static final String USE_ICONS = "Use Icons";
private static final String DEFAULT_LAYOUT_ALGORITHM = "Default Layout Algorithm";
private static final String EDGE_COLORS = "Edge Colors";
private static final String VERTEX_COLORS = "Vertex Colors";
private static final String VERTEX_SHAPES = "Vertex Shapes";
private static final String MISCELLANIOUS_OPTIONS = "Miscellanious";
private static final String DEFAULT_VERTEX_COLOR = "Default Vertex Color";
private static final String DEFAULT_EDGE_COLOR = "Default Edge Color";
private static final String DEFAULT_VERTEX_SHAPE = "Default Vertex Shape";
private static final String FAVORED_EDGE_TYPE = "Favored Edge Type";
private static final String VERTEX_SELECTION_COLOR = "Selected Vertex Color";
private static final String EDGE_SELECTION_COLOR = "Selected Edge Color";
private GraphType graphType;
private Map<String, Color> vertexColorMap = new HashMap<>();
private Map<String, Color> edgeColorMap = new HashMap<>();
private Map<String, VertexShape> vertexShapeMap = new HashMap<>();
private Map<String, Integer> edgePriorityMap = new HashMap<>();
private List<ChangeListener> changeListeners = new CopyOnWriteArrayList<>();
private Color vertexSelectionColor = Color.green;
private Color edgeSelectionColor = Color.green;
private Color defaultVertexColor = Color.blue;
private Color defaultEdgeColor = Color.blue;
private String favoredEdgeType;
private VertexShape defaultVertexShape = VertexShape.RECTANGLE;
private String vertexLabelOverride = null;
private String vertexColorOverride = null;
private String vertexShapeOverride = null;
private String edgeColorOverride = null;
private final String rootOptionsName;
private boolean registeredWithTool = false;
private String defaultLayoutAlgorithmName = LayoutAlgorithmNames.MIN_CROSS_COFFMAN_GRAHAM;
private boolean useIcons = true;
private GraphLabelPosition labelPosition = GraphLabelPosition.SOUTH;
private Font font = new Font("Dialog", Font.BOLD, 18);
private int arrowLength = 15;
/**
* Constructs a new GraphTypeDisplayOptions for the given {@link GraphType}
* @param graphType The {@link GraphType} for which to define display options
*/
public GraphDisplayOptions(GraphType graphType) {
this(graphType, null);
}
/**
* Constructs a new GraphTypeDisplayOptions for the given {@link GraphType} and initializes
* from tool options. Note this form should only be used for display options on
* {@link GraphType}s that have options registered in the tool.
* @param graphType The {@link GraphType} for which to define display options
* @param tool the tool from which to initialize from {@link ToolOptions}
*/
public GraphDisplayOptions(GraphType graphType, Tool tool) {
this.graphType = graphType;
rootOptionsName = graphType.getOptionsName();
List<String> edgeTypes = graphType.getEdgeTypes();
if (!edgeTypes.isEmpty()) {
favoredEdgeType = edgeTypes.iterator().next();
}
initializeEdgePriorities();
initializeDefaults();
initializeFromOptions(tool);
}
private void initializeEdgePriorities() {
// initialize priorities based on the order they were defined
for (String edgeType : graphType.getEdgeTypes()) {
edgePriorityMap.put(edgeType, edgePriorityMap.size());
}
}
protected void initializeDefaults() {
// Overridden by subclass to define defaultValues
}
/**
* Adds a ChangeListener to be notified when display options change
* @param listener the listener to be notified.
*/
public void addChangeListener(ChangeListener listener) {
changeListeners.add(listener);
}
/**
* Removes the listener so that it won't be notified of changes any longer
* @param listener the listener to be removed
*/
public void removeChangeListener(ChangeListener listener) {
changeListeners.remove(listener);
}
/**
* Sets the default shape to be used by vertices that don't have a vertex type set
* @param shape the default vertex shape
*/
public void setDefaultVertexShape(VertexShape shape) {
this.defaultVertexShape = Objects.requireNonNull(shape);
}
/**
* Sets the default color to be used by vertices that don't have a vertex type set
* @param color the default vertex shape
*/
public void setDefaultVertexColor(Color color) {
this.defaultVertexColor = Objects.requireNonNull(color);
}
/**
* Sets the default color to be used by edges that don't have a edge type set
* @param color the default edge shape
*/
public void setDefaultEdgeColor(Color color) {
this.defaultEdgeColor = Objects.requireNonNull(color);
}
/**
* Returns the default color for edges that don't have an edge type set
* @return the default color for edges that don't have an edge type set
*/
public Color getDefaultEdgeColor() {
return defaultEdgeColor;
}
/**
* Returns the default color for vertices that don't have an vertex type set
* @return the default color for vertices that don't have an vertex type set
*/
public Color getDefaultVertexColor() {
return defaultVertexColor;
}
/**
* Sets the attribute key that can be used to override the label text shown for the vertex.
* Normally, the vertex's name is shown as the label.
* @param attributeKey the attribute key that, if set, will be used to define the vertice's label
*/
public void setVertexLabelOverrideAttributeKey(String attributeKey) {
vertexLabelOverride = attributeKey;
}
/**
* Returns the attribute key that can override the vertices label text
* @return the attribute key that can override the vertices label text
*/
public String getVertexLabelOverride() {
return vertexLabelOverride;
}
/**
* Sets the attribute key that can be used to override the color for a vertex. Normally, the
* color is determined by the vertex type, which will be mapped to a color
* @param attributeKey the attribute key that, if set, will be used to define the vertice's color
*/
public void setVertexColorOverrideAttributeKey(String attributeKey) {
vertexColorOverride = attributeKey;
}
/**
* Sets the attribute key that can be used to override the color for an edge. Normally, the
* color is determined by the edge type, which will be mapped to a color
* @param attributeKey the attribute key that, if set, will be used to define the edge's color
*/
public void setEdgeColorOverrideAttributeKey(String attributeKey) {
edgeColorOverride = attributeKey;
}
/**
* Returns the attribute key that can be used to override the color of an edge
* @return the attribute key that can be used to override the color of an edge
*/
public String getEdgeColorOverrideAttributeKey() {
return edgeColorOverride;
}
/**
* Sets the attribute key that can be used to override the shape for a vertex. Normally, the
* shape is determined by the vertex type, which will be mapped to a shape
* @param attributeKey the attribute key that, if set, will be used to define the vertice's shape
*/
public void setVertexShapeOverrideAttributeKey(String attributeKey) {
vertexShapeOverride = attributeKey;
}
/**
* Returns the text that will be displayed as the label for the given vertex
* @param vertex the vertex for which to get label text
* @return the text that will be displayed as the label for the given vertex
*/
public String getVertexLabel(AttributedVertex vertex) {
String vertexLabel = null;
if (vertexLabelOverride != null) {
vertexLabel = vertex.getAttribute(vertexLabelOverride);
}
if (vertexLabel == null) {
vertexLabel = vertex.getName();
}
if (vertexLabel.contains("\n")) {
vertexLabel = StringEscapeUtils.escapeHtml4(vertexLabel);
return "<html>" + String.join("<p>", Splitter.on('\n').split(vertexLabel));
}
return vertexLabel;
}
/**
* Returns the {@link VertexShape} that will be used to draw the vertex's shape
* @param vertex the vertex for which to get the shape
* @return the {@link VertexShape} that will be used to draw the vertex's shape
*/
public VertexShape getVertexShape(AttributedVertex vertex) {
if (vertexShapeOverride != null) {
String shapeName = vertex.getAttribute(vertexShapeOverride);
if (shapeName != null) {
VertexShape shape = VertexShape.getShape(shapeName);
if (shape != null) {
return shape;
}
}
}
String vertexType = vertex.getVertexType();
return vertexShapeMap.getOrDefault(vertexType, defaultVertexShape);
}
/**
* Returns the color that will be used to draw the vertex
* @param vertex the vertex for which to get the color
* @return the color that will be used to draw the vertex
*/
public Color getVertexColor(AttributedVertex vertex) {
if (vertexColorOverride != null) {
String colorValue = vertex.getAttribute(vertexColorOverride);
if (colorValue != null) {
Color color = WebColors.getColor(colorValue);
if (color != null) {
return color;
}
}
}
String vertexType = vertex.getVertexType();
return vertexColorMap.getOrDefault(vertexType, defaultVertexColor);
}
/**
* Returns the color that will be used to draw the edge
* @param edge the edge for which to get the color
* @return the color that will be used to draw the edge
*/
public Color getEdgeColor(AttributedEdge edge) {
if (edgeColorOverride != null) {
String colorValue = edge.getAttribute(edgeColorOverride);
if (colorValue != null) {
Color color = WebColors.getColor(colorValue);
if (color != null) {
return color;
}
}
}
String edgeType = edge.getEdgeType();
return edgeColorMap.getOrDefault(edgeType, defaultEdgeColor);
}
/**
* Returns the priority for the given edge type. This is used by layout algorithms to
* determine which edges should have more influence on the layout.
* @param edgeType the edge type for which to get it's priority
* @return the priority for the given edge type
*/
public Integer getEdgePriority(String edgeType) {
return edgePriorityMap.getOrDefault(edgeType, Integer.MAX_VALUE);
}
/**
* Returns the edge type that is the preferred edge for layout purposes
* @return the edge type that is the preferred edge for layout purposes
*/
public String getFavoredEdgeType() {
return favoredEdgeType;
}
/**
* Sets the favored edge type. The favored edge type is used to influence layout algorithms
* @param favoredEdgeType the edge type that is to be favored by layout algorithms
*/
public void setFavoredEdgeType(String favoredEdgeType) {
checkEdgeType(favoredEdgeType);
this.favoredEdgeType = favoredEdgeType;
}
/**
* Returns the {@link GraphType} that this object provides display options for
* @return the {@link GraphType} that this object provides display options for
*/
public GraphType getGraphType() {
return graphType;
}
/**
* Returns the color for the given vertex type
* @param vertexType the vertex type to get the color for
* @return the color for the given vertex type
*/
public Color getVertexColor(String vertexType) {
return vertexColorMap.getOrDefault(vertexType, defaultVertexColor);
}
/**
* Sets the color for vertices with the given vertex type
* @param vertexType the vertex type for which to set its color
* @param color the color to use for vertices with the given vertex type
*/
public void setVertexColor(String vertexType, Color color) {
checkVertexType(vertexType);
vertexColorMap.put(vertexType, Objects.requireNonNull(color));
}
private String getVertexShapeName(String vertexType) {
VertexShape vertexShape = vertexShapeMap.getOrDefault(vertexType, defaultVertexShape);
return vertexShape.getName();
}
/**
* Sets the {@link VertexShape} to use for vertices with the given vertex type
* @param vertexType the vertex type for which to set its shape
* @param vertexShape the {@link VertexShape} to use for vertices with the given vertex type
*/
public void setVertexShape(String vertexType, VertexShape vertexShape) {
checkVertexType(vertexType);
vertexShapeMap.put(vertexType, Objects.requireNonNull(vertexShape));
}
/**
* Returns the color for the given edge type
* @param edgeType the edge type whose color is to be determined.
* @return the color for the given edge type.
*/
public Color getEdgeColor(String edgeType) {
return edgeColorMap.getOrDefault(edgeType, defaultEdgeColor);
}
/**
* Sets the color for edges with the given edge type
* @param edgeType the edge type for which to set its color
* @param color the new color for edges with the given edge type
*/
public void setEdgeColor(String edgeType, Color color) {
checkEdgeType(edgeType);
edgeColorMap.put(edgeType, Objects.requireNonNull(color));
}
@Override
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
Object newValue) throws OptionsVetoException {
if (optionName.startsWith(rootOptionsName)) {
updateOptions(options.getOptions(rootOptionsName));
}
notifyListeners();
}
/**
* Returns the name for the root Options name for this {@link GraphDisplayOptions}
* @return the name for the root Options name for this {@link GraphDisplayOptions}
*/
public String getRootOptionsName() {
return rootOptionsName;
}
/**
* Returns the attribute key that can be used to override the color of a vertex. Normally,
* a vertex is colored based on its vertex type. However, if this value is non-null, a vertex
* can override its color by setting an attribute using this key name.
* @return the attribute key that can be used to override the color of a vertex
*/
public String getVertexColorOverrideAttributeKey() {
return vertexColorOverride;
}
/**
* Returns the attribute key that can be used to override the shape of a vertex. Normally,
* a vertex has a shape based on its vertex type. However, if this value is non-null, a vertex
* can override its shape by setting an attribute using this key name.
* @return the attribute key that can be used to override the shape of a vertex
*/
public String getVertexShapeOverrideAttributeKey() {
return vertexShapeOverride;
}
/**
* returns the {@link VertexShape} for any vertex that has not vertex type defined
* @return the {@link VertexShape} for any vertex that has not vertex type defined
*/
public VertexShape getDefaultVertexShape() {
return defaultVertexShape;
}
/**
* Returns the {@link VertexShape} for vertices that have the given vertex type
* @param vertexType the vertex type for which to get its asigned shape
* @return the {@link VertexShape} for vertices that have the given vertex type
*/
public VertexShape getVertexShape(String vertexType) {
return vertexShapeMap.getOrDefault(vertexType, defaultVertexShape);
}
/**
* Returns the vertex selection color
* @return the vertex selection color
*/
public Color getVertexSelectionColor() {
return vertexSelectionColor;
}
/**
* Sets the vertex selection color
* @param vertexSelectionColor the color to use for highlighting selected vertices
*/
public void setVertexSelectionColor(Color vertexSelectionColor) {
this.vertexSelectionColor = vertexSelectionColor;
}
/**
* Returns the color for edge selections
* @return the color fore edge selections
*/
public Color getEdgeSelectionColor() {
return edgeSelectionColor;
}
/**
* Sets the edge selection color
* @param edgeSelectionColor color to use for highlighting selected edges
*/
public void setEdgeSelectionColor(Color edgeSelectionColor) {
this.edgeSelectionColor = edgeSelectionColor;
}
/**
* Returns the name of the default graph layout algorithm
* @return the name of the default graph layout algorithms
*/
public String getDefaultLayoutAlgorithmNameLayout() {
return defaultLayoutAlgorithmName;
}
/**
* Sets the name of the default layout algorithm
* @param defaultLayout the name of the layout algorithm to use by default
*/
public void setDefaultLayoutAlgorithmName(String defaultLayout) {
this.defaultLayoutAlgorithmName = defaultLayout;
}
/**
* Returns true if the rendering mode is to use icons for the vertices. If using
* icons, the label is drawn inside the shape.
* @return true if the rendering mode is to use icons.
*/
public boolean usesIcons() {
return useIcons;
}
/**
* Sets whether the graph rendering mode is to use icons or not. If using icons, the label and
* shape are drawn together into a cached icon. Otherwise, the shapes are drawn on the fly and
* labeled separately.
* @param b true to render in icon mode.
*/
public void setUsesIcons(boolean b) {
this.useIcons = b;
}
/**
* Returns the label position relative to the vertex. Note this is only relevant
* if {@link #usesIcons()} is false
* @return the label position relative to the vertex
*/
public GraphLabelPosition getLabelPosition() {
return labelPosition;
}
/**
* Sets the label position relative to the vertex. Note this is only relevant
* if {@link #usesIcons()} is false.
* @param labelPosition the {@link GraphLabelPosition} to use for rendering vertex labels
*/
public void setLabelPosition(GraphLabelPosition labelPosition) {
this.labelPosition = labelPosition;
}
/**
* Sets the font to use for drawing vertex labels
* @param font the font to use for drawing vertex labels
*/
public void setFont(Font font) {
this.font = font;
}
/**
* Returns the font being used to render vertex labels
* @return the font being used to render vertex labels
*/
public Font getFont() {
return font;
}
/**
* Returns the length of the arrow. The width will be proportional to the length.
* Note: this option is not exposed in the Options because it is too specific to a graph
* instance and wouldn't be appropriate to apply to shared options.
* @return the size if the arrow
*/
public int getArrowLength() {
return arrowLength;
}
/**
* Sets the length of the arrow. The width will be proportional to the length.
* Note: this option is not exposed in the Options because it is too specific to a graph
* instance and wouldn't be appropriate to apply to shared options.
* @param length the size of the arrow
*/
public void setArrowLength(int length) {
this.arrowLength = length;
}
/**
* Returns true if this {@link GraphDisplayOptions} instance has been constructed with
* a tool for getting/saving option values in the tool options
* @return true if this {@link GraphDisplayOptions} instance is connected to tool options
*/
public boolean isRegisteredWithTool() {
return registeredWithTool;
}
/**
* Registers this GraphTypeDisplayOptions with {@link ToolOptions}. Note: this should only
* be used by plugins or other objects that get instantiated immediately when the tool is
* constructed. Otherwise, if the tool exits and this hasn't been called, any saved option
* values will be lost.
* <P>
* @param toolOptions the {@link ToolOptions} to register these options with
* @param help the help location to be used by the {@link OptionsDialog} for display/editing
* these options
*/
public void registerOptions(ToolOptions toolOptions, HelpLocation help) {
Options rootOptions = toolOptions.getOptions(graphType.getOptionsName());
registerVertexColorOptions(rootOptions, help);
registerVertexShapeOptions(rootOptions, help);
registerEdgeColorOptions(rootOptions, help);
registerMiscellaniousOptions(rootOptions, help);
}
/**
* Sets default values for vertex types
* @param vertexType the vertex type whose default color and shape are being defined
* @param vertexShape the default vertex shape for the given vertex type
* @param color the default color for the given vertex type
*/
protected void configureVertexType(String vertexType, VertexShape vertexShape, Color color) {
checkVertexType(vertexType);
vertexShapeMap.put(vertexType, vertexShape);
vertexColorMap.put(vertexType, color);
}
/**
* Sets default values for edge types
* @param edgeType the edge type whose default color and shape are being defined
* @param color the default color for the given edge type
*/
protected void configureEdgeType(String edgeType, Color color) {
checkEdgeType(edgeType);
edgeColorMap.put(edgeType, color);
}
/**
* Loads values from tool options
*
* @param tool the tool from which to update values.
*/
public void initializeFromOptions(Tool tool) {
if (tool == null) {
return;
}
ToolOptions toolOptions = tool.getOptions("Graph");
toolOptions.addOptionsChangeListener(this);
updateOptions(toolOptions.getOptions(rootOptionsName));
registeredWithTool = true;
}
private void updateOptions(Options rootOptions) {
updateVertexColorsFromOptions(rootOptions);
updateEdgeColorsFromOptions(rootOptions);
updateVertexShapesFromOptions(rootOptions);
updateMiscellaniousOptions(rootOptions);
}
private void updateMiscellaniousOptions(Options rootOptions) {
Options options = rootOptions.getOptions(MISCELLANIOUS_OPTIONS);
String shapeName = options.getString(DEFAULT_VERTEX_SHAPE, defaultVertexShape.getName());
defaultVertexShape = VertexShape.getShape(shapeName);
defaultVertexColor = options.getColor(DEFAULT_VERTEX_COLOR, defaultVertexColor);
defaultEdgeColor = options.getColor(DEFAULT_EDGE_COLOR, defaultEdgeColor);
favoredEdgeType = options.getString(FAVORED_EDGE_TYPE, favoredEdgeType);
vertexSelectionColor = options.getColor(VERTEX_SELECTION_COLOR, vertexSelectionColor);
edgeSelectionColor = options.getColor(EDGE_SELECTION_COLOR, edgeSelectionColor);
defaultLayoutAlgorithmName =
options.getString(DEFAULT_LAYOUT_ALGORITHM, defaultLayoutAlgorithmName);
useIcons = options.getBoolean(USE_ICONS, useIcons);
labelPosition = options.getEnum(LABEL_POSITION, labelPosition);
font = options.getFont(FONT, font);
}
private void updateVertexShapesFromOptions(Options rootOptions) {
Options options = rootOptions.getOptions(VERTEX_SHAPES);
for (String vertexType : graphType.getVertexTypes()) {
String current = getVertexShapeName(vertexType);
String shapeName = options.getString(vertexType, current);
if (shapeName != null && !shapeName.equals(current)) {
VertexShape shape = VertexShape.getShape(shapeName);
if (shape != null) {
setVertexShape(vertexType, VertexShape.getShape(shapeName));
}
}
}
}
private void updateEdgeColorsFromOptions(Options rootOptions) {
Options options = rootOptions.getOptions(EDGE_COLORS);
for (String edgeType : graphType.getEdgeTypes()) {
Color current = getEdgeColor(edgeType);
Color color = options.getColor(edgeType, current);
if (color != null && !color.equals(current)) {
setEdgeColor(edgeType, color);
}
}
}
private void notifyListeners() {
for (ChangeListener changeListener : changeListeners) {
changeListener.stateChanged(new ChangeEvent(this));
}
}
private void updateVertexColorsFromOptions(Options rootOptions) {
Options options = rootOptions.getOptions(VERTEX_COLORS);
for (String vertexType : graphType.getVertexTypes()) {
Color current = getVertexColor(vertexType);
Color color = options.getColor(vertexType, current);
if (color != null && !color.equals(current)) {
setVertexColor(vertexType, color);
}
}
}
private void registerVertexColorOptions(Options rootOptions, HelpLocation help) {
Options options = rootOptions.getOptions(VERTEX_COLORS);
for (String vertexType : graphType.getVertexTypes()) {
options.registerOption(vertexType, OptionType.COLOR_TYPE,
getVertexColor(vertexType), help,
"Choose the color for this vertex type");
}
List<String> list = new ArrayList<>(graphType.getVertexTypes());
OptionsEditor editor = new ScrollableOptionsEditor(VERTEX_COLORS, list);
options.registerOptionsEditor(editor);
}
private void registerVertexShapeOptions(Options rootOptions, HelpLocation help) {
Options options = rootOptions.getOptions(VERTEX_SHAPES);
List<String> shapeNames = VertexShape.getShapeNames();
for (String vertexType : graphType.getVertexTypes()) {
StringWithChoicesEditor editor = new StringWithChoicesEditor(shapeNames);
options.registerOption(vertexType, OptionType.STRING_TYPE,
getVertexShapeName(vertexType), help,
"Choose the shape for this vertex type", editor);
}
List<String> list = new ArrayList<>(graphType.getVertexTypes());
OptionsEditor editor = new ScrollableOptionsEditor(VERTEX_SHAPES, list);
options.registerOptionsEditor(editor);
}
private void registerEdgeColorOptions(Options rootOptions, HelpLocation help) {
Options options = rootOptions.getOptions(EDGE_COLORS);
for (String edgeType : graphType.getEdgeTypes()) {
options.registerOption(edgeType, OptionType.COLOR_TYPE,
getEdgeColor(edgeType), help, "Choose the color for this edge type");
}
List<String> list = new ArrayList<>(graphType.getEdgeTypes());
OptionsEditor editor = new ScrollableOptionsEditor(EDGE_COLORS, list);
options.registerOptionsEditor(editor);
}
private void registerMiscellaniousOptions(Options rootOptions, HelpLocation help) {
Options options = rootOptions.getOptions(MISCELLANIOUS_OPTIONS);
StringWithChoicesEditor editor = new StringWithChoicesEditor(VertexShape.getShapeNames());
options.registerOption(VERTEX_SELECTION_COLOR, OptionType.COLOR_TYPE, vertexSelectionColor,
help, "Color for highlighting selected vertices");
options.registerOption(EDGE_SELECTION_COLOR, OptionType.COLOR_TYPE, edgeSelectionColor,
help, "Color for highlighting selected edge");
options.registerOption(DEFAULT_VERTEX_SHAPE, OptionType.STRING_TYPE,
defaultVertexShape.getName(),
help, "Shape for vertices that have no vertex type defined", editor);
options.registerOption(DEFAULT_VERTEX_COLOR, OptionType.COLOR_TYPE, defaultVertexColor,
help, "Color for vertices that have no vertex type defined");
options.registerOption(DEFAULT_EDGE_COLOR, OptionType.COLOR_TYPE, defaultEdgeColor,
help, "Color for edge that have no edge type defined");
editor = new StringWithChoicesEditor(graphType.getEdgeTypes());
options.registerOption(FAVORED_EDGE_TYPE, OptionType.STRING_TYPE, favoredEdgeType, help,
"Favored edge is used to influence layout algorithms", editor);
editor = new StringWithChoicesEditor(LayoutAlgorithmNames.getLayoutAlgorithmNames());
options.registerOption(DEFAULT_LAYOUT_ALGORITHM, OptionType.STRING_TYPE,
defaultLayoutAlgorithmName, help, "Initial layout algorithm", editor);
options.registerOption(USE_ICONS, OptionType.BOOLEAN_TYPE, useIcons, help,
"If true, vertices are drawn using pre-rendered images versus compact shapes");
options.registerOption(LABEL_POSITION, OptionType.ENUM_TYPE, labelPosition, help,
"Relative postion of labels to vertex shape (Only applicable if \"Use Icons\" is true");
options.registerOption(FONT, OptionType.FONT_TYPE, font, help,
"Font to use for vertex labels");
List<String> optionNamesInDisplayOrder = new ArrayList<>();
optionNamesInDisplayOrder.add(VERTEX_SELECTION_COLOR);
optionNamesInDisplayOrder.add(EDGE_SELECTION_COLOR);
optionNamesInDisplayOrder.add(DEFAULT_VERTEX_COLOR);
optionNamesInDisplayOrder.add(DEFAULT_EDGE_COLOR);
optionNamesInDisplayOrder.add(DEFAULT_VERTEX_SHAPE);
optionNamesInDisplayOrder.add(FAVORED_EDGE_TYPE);
optionNamesInDisplayOrder.add(DEFAULT_LAYOUT_ALGORITHM);
optionNamesInDisplayOrder.add(LABEL_POSITION);
optionNamesInDisplayOrder.add(FONT);
optionNamesInDisplayOrder.add(USE_ICONS);
OptionsEditor optionsEditor =
new ScrollableOptionsEditor(MISCELLANIOUS_OPTIONS, optionNamesInDisplayOrder);
options.registerOptionsEditor(optionsEditor);
}
private void checkVertexType(String vertexType) {
if (!getGraphType().containsVertexType(vertexType)) {
throw new IllegalArgumentException("VertexType \"" + vertexType +
"\" not defined in GraphType \"" + getGraphType().getName() + "\".");
}
}
private void checkEdgeType(String edgeType) {
if (!getGraphType().containsEdgeType(edgeType)) {
throw new IllegalArgumentException("EdgeType \"" + edgeType +
"\" not defined in GraphType \"" + getGraphType().getName() + "\".");
}
}
}

View file

@ -0,0 +1,193 @@
/* ###
* 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.service.graph;
import java.awt.Color;
import java.util.Objects;
/**
* Builder for building {@link GraphDisplayOptions}
*/
public class GraphDisplayOptionsBuilder {
private GraphDisplayOptions displayOptions;
/**
* Create a new {@link GraphDisplayOptionsBuilder}
* @param graphType the {@link GraphType} of graphs that this instance configures.
*/
public GraphDisplayOptionsBuilder(GraphType graphType) {
displayOptions = new GraphDisplayOptions(graphType);
}
/**
* Sets the default vertex color for vertexes that don't have a registered vertex type
* @param c the default vertex color
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder defaultVertexColor(Color c) {
displayOptions.setDefaultVertexColor(c);
return this;
}
/**
* Sets the default edge color for edges that don't have a registered edge type
* @param c the default edge color
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder defaultEdgeColor(Color c) {
Objects.requireNonNull(c);
displayOptions.setDefaultEdgeColor(c);
return this;
}
/**
* Sets the vertex selection color
* @param color the vertex selection color
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder vertexSelectionColor(Color color) {
displayOptions.setVertexSelectionColor(color);
return this;
}
/**
* Sets the edge selection color
* @param color the edge selection color
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder edgeSelectionColor(Color color) {
displayOptions.setEdgeSelectionColor(color);
return this;
}
/**
* Sets the default vertex shape for vertices that don't have a registered vertex type
* @param vertexShape the {@link VertexShape} to use as a default
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder defaultVertexShape(VertexShape vertexShape) {
Objects.requireNonNull(vertexShape);
displayOptions.setDefaultVertexShape(vertexShape);
return this;
}
/**
* Sets the shape and color for vertices of the given type
* @param vertexType the vertex type to assign shape and color
* @param vertexShape the shape to use for the named vertex type
* @param color the color to use for the named vertex type
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder vertex(String vertexType, VertexShape vertexShape,
Color color) {
displayOptions.configureVertexType(vertexType, vertexShape, color);
return this;
}
/**
* Sets the color for edges of the given type
* @param edgeType the edge type to assign color
* @param color the color to use for the named edge type
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder edge(String edgeType, Color color) {
displayOptions.configureEdgeType(edgeType, color);
return this;
}
/**
* Sets the attribute used to override the color for a vertex
* @param colorAttributeKey the attribute key to use for overriding a vertex color
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder vertexColorOverrideAttribute(String colorAttributeKey) {
displayOptions.setVertexColorOverrideAttributeKey(colorAttributeKey);
return this;
}
/**
* Sets the attribute used to override the color for a edge
* @param colorAttributeKey the attribute key to use for overriding an edge color
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder edgeColorOverrideAttribute(String colorAttributeKey) {
displayOptions.setEdgeColorOverrideAttributeKey(colorAttributeKey);
return this;
}
/**
* Sets the attribute used to override the shape for a vertex
* @param shapeAttributeKey the attribute key to use of shape override
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder shapeOverrideAttribute(String shapeAttributeKey) {
displayOptions.setVertexShapeOverrideAttributeKey(shapeAttributeKey);
return this;
}
/**
* Sets the name of the layout algorithm that will be used to initially layout the graph
* @param string the name of the layout algoritm to use to initially layout the graph
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder defaultLayoutAlgorithm(String string) {
displayOptions.setDefaultLayoutAlgorithmName(string);
return this;
}
/**
* Sets drawing "mode" for the graph display. If true, vertices are drawn as scaled
* cached images with the label inside the shapes. If false, vertices are drawn as smaller
* shapes with labels drawn near the shapes.
* @param b true to use pre-rendered icon images
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder useIcons(boolean b) {
displayOptions.setUsesIcons(b);
return this;
}
/**
* Sets the length of the arrows to display in the graph. The width will be sized proportionately.
* @param length the length the arrows to display in the graph
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder arrowLength(int length) {
displayOptions.setArrowLength(length);
return this;
}
/**
* Sets the vertex label position relative to vertex shape. This is only applicable if the
* {@link #useIcons(boolean)} is set to true.
* @param labelPosition the relative position to place the vertex label
* @return this {@link GraphDisplayOptionsBuilder}
*/
public GraphDisplayOptionsBuilder labelPosition(GraphLabelPosition labelPosition) {
displayOptions.setLabelPosition(labelPosition);
return this;
}
/**
* Returns a GraphTypeDisplayOptions as configured by this builder
* @return a GraphTypeDisplayOptions as configured by this builder
*/
public GraphDisplayOptions build() {
return displayOptions;
}
}

View file

@ -0,0 +1,23 @@
/* ###
* 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.service.graph;
/**
* Specification for the vertex label position relative to the vertex shape.
*/
public enum GraphLabelPosition {
NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST, CENTER
}

View file

@ -0,0 +1,140 @@
/* ###
* 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.service.graph;
import java.util.*;
/**
* Class that defines a new graph type. It defines the set of valid vertex and edge types
*/
public class GraphType {
private final String name;
private final String description;
private final Set<String> vertexTypes;
private final Set<String> edgeTypes;
/**
* Constructs a new GraphType
*
* @param name the name of this GraphType instance
* @param description a brief description for graphs of this type
* @param vertexTypes a list of all valid vertex types for graphs of this type
* @param edgeTypes a list of all valid edge types for graphs of this type
*/
public GraphType(String name, String description, List<String> vertexTypes,
List<String> edgeTypes) {
this.name = Objects.requireNonNull(name);
this.description = Objects.requireNonNull(description);
this.vertexTypes = Collections.unmodifiableSet(new LinkedHashSet<String>(vertexTypes));
this.edgeTypes = Collections.unmodifiableSet(new LinkedHashSet<String>(edgeTypes));
}
/**
* Returns a name for this type of graph
* @return a name of this type of graph
*/
public String getName() {
return name;
}
/**
* Returns a description for this type of graph
* @return a description for this type of graph
*/
public String getDescription() {
return description;
}
/**
* Returns a list of valid vertex types for graphs of this type
* @return a list of valid vertex types for graphs of this type
*/
public List<String> getVertexTypes() {
return new ArrayList<>(vertexTypes);
}
/**
* Returns a list of valid edge types for graphs of this type
* @return a list of valid edge types for graphs of this type
*/
public List<String> getEdgeTypes() {
return new ArrayList<>(edgeTypes);
}
/**
* Test if the given string is a valid vertex type
* @param vertexType the string to test for being a valid vertex type
* @return true if the given string is a valid vertex type
*/
public boolean containsVertexType(String vertexType) {
return vertexTypes.contains(vertexType);
}
/**
* Test if the given string is a valid edge type
* @param edgeType the string to test for being a valid edge type
* @return true if the given string is a valid edge type
*/
public boolean containsEdgeType(String edgeType) {
return edgeTypes.contains(edgeType);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((description == null) ? 0 : description.hashCode());
result = prime * result + ((edgeTypes == null) ? 0 : edgeTypes.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((vertexTypes == null) ? 0 : vertexTypes.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
GraphType other = (GraphType) obj;
if (!name.equals(other.name)) {
return false;
}
if (!description.equals(other.description)) {
return false;
}
if (!edgeTypes.equals(other.edgeTypes)) {
return false;
}
if (!vertexTypes.equals(other.vertexTypes)) {
return false;
}
return true;
}
public String getOptionsName() {
return getName() + " Graph Type";
}
}

View file

@ -0,0 +1,76 @@
/* ###
* 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.service.graph;
import java.util.ArrayList;
import java.util.List;
/**
* Builder class for building new {@link GraphType}s
*/
public class GraphTypeBuilder {
private List<String> vertexTypes = new ArrayList<>();
private List<String> edgeTypes = new ArrayList<>();
private final String name;
private String description;
/**
* Create a new builder
* @param name the name of the new {@link GraphType}
*/
public GraphTypeBuilder(String name) {
this.name = name;
this.description = name;
}
/**
* Sets the description for the {@link GraphType}
* @param text the description
* @return this GraphTypeBuilder
*/
public GraphTypeBuilder description(String text) {
this.description = text;
return this;
}
/**
* Defines a new vertex type
* @param type a string that names a new vertex type
* @return this GraphTypeBuilder
*/
public GraphTypeBuilder vertexType(String type) {
vertexTypes.add(type);
return this;
}
/**
* Defines a new edge type
* @param type a string that names a new edge type
* @return this GraphTypeBuilder
*/
public GraphTypeBuilder edgeType(String type) {
edgeTypes.add(type);
return this;
}
/**
* Builds a new GraphType
* @return a new GraphType
*/
public GraphType build() {
return new GraphType(name, description, vertexTypes, edgeTypes);
}
}

View file

@ -0,0 +1,56 @@
/* ###
* 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.service.graph;
import java.util.Arrays;
import java.util.List;
/**
* Just a static list of graph layout algorithm names
*/
public class LayoutAlgorithmNames {
//@formatter:off
public static final String FORCED_BALANCED = "Force Balanced";
public static final String FORCE_DIRECTED = "Force Directed";
public static final String CIRCLE = "Circle";
public static final String COMPACT_HIERACHICAL = "Compact Hierarchical";
public static final String COMPACT_RADIAL = "Compact Radial";
public static final String MIN_CROSS_TOP_DOWN = "Hierarchical MinCross Top Down";
public static final String MIN_CROSS_LONGEST_PATH = "Hierarchical MinCross Longest Path";
public static final String MIN_CROSS_NETWORK_SIMPLEX = "Hierarchical MinCross Network Simplex";
public static final String MIN_CROSS_COFFMAN_GRAHAM = "Hierarchical MinCross Coffman Graham";
public static final String VERT_MIN_CROSS_TOP_DOWN = "Vertical Hierarchical MinCross Top Down";
public static final String VERT_MIN_CROSS_LONGEST_PATH ="Vertical Hierarchical MinCross Longest Path";
public static final String VERT_MIN_CROSS_NETWORK_SIMPLEX ="Vertical Hierarchical MinCross Network Simplex";
public static final String VERT_MIN_CROSS_COFFMAN_GRAHAM ="Vertical Hierarchical MinCross Coffman Graham";
public static final String HIERACHICAL = "Hierarchical";
public static final String RADIAL = "Radial";
public static final String BALLOON = "Balloon";
public static final String GEM = "GEM";
//@formatter:on
public static List<String> getLayoutAlgorithmNames() {
return Arrays.asList(COMPACT_HIERACHICAL, HIERACHICAL,
COMPACT_RADIAL, MIN_CROSS_TOP_DOWN, MIN_CROSS_LONGEST_PATH,
MIN_CROSS_NETWORK_SIMPLEX, MIN_CROSS_COFFMAN_GRAHAM, CIRCLE,
VERT_MIN_CROSS_TOP_DOWN,
VERT_MIN_CROSS_LONGEST_PATH,
VERT_MIN_CROSS_NETWORK_SIMPLEX,
VERT_MIN_CROSS_COFFMAN_GRAHAM,
FORCED_BALANCED, FORCE_DIRECTED, RADIAL, BALLOON, GEM);
}
}

View file

@ -0,0 +1,339 @@
/* ###
* 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.service.graph;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.*;
import java.util.*;
/**
* Class for defining shapes to use for rendering vertices in a graph
*/
public abstract class VertexShape {
private static Map<String, VertexShape> registeredShapes = new HashMap<>();
private static int SIZE = 50;
public static VertexShape RECTANGLE = new RectangleVertexShape(SIZE);
public static VertexShape ELLIPSE = new EllipseVertexShape(SIZE);
public static VertexShape TRIANGLE_UP = new TriangleUpVertexShape(SIZE);
public static VertexShape TRIANGLE_DOWN = new TriangleDownVertexShape(SIZE);
public static VertexShape STAR = new StarVertexShape(SIZE);
public static VertexShape DIAMOND = new DiamondVertexShape(SIZE);
public static VertexShape PENTAGON = new PentagonVertexShape(SIZE);
public static VertexShape HEXAGON = new HexagonVertexShape(SIZE);
public static VertexShape OCTAGON = new OctagonVertexShape(SIZE);
private Shape cachedShape;
private String name;
private int size;
VertexShape(String name, int size) {
this.name = name;
this.size = size;
registeredShapes.put(name, this);
}
/**
* Returns the name of the shape
* @return the name of the shape
*/
public String getName() {
return name;
}
/**
* Returns the {@link Shape} for this {@link VertexShape} instance
* @return the {@link Shape} for this {@link VertexShape} instance
*/
public Shape getShape() {
if (cachedShape == null) {
cachedShape = size(createShape());
}
return cachedShape;
}
private Shape size(Shape shape) {
AffineTransform transform = new AffineTransform();
Rectangle bounds = shape.getBounds();
double scale = size / bounds.getWidth();
transform.scale(scale, scale);
return transform.createTransformedShape(shape);
}
/**
* Gets the relative amount of margin space to allocate above the label. The default is
* 0.5 which will center the label in the associated shape. A value closer to 0 will move
* the label closer to the top and a value closer to 1 will move the label closer to the
* bottom.
* @return the relative amount of margin space to allocate obove the label.s
*/
public double getLabelPosition() {
return .5;
}
/**
* Returns the size factor for a shape relative to its label. Shapes are sized based on the
* label of a vertex so that the label can fit inside the shape (mostly). Some subclasses
* will need to override this value to some value > 1 to fit the label in the shape. For
* example, a rectangle shape does not need to be extended because text naturally fits. But
* for a shape like a triangle, its bounding box needs to be bigger so that text doesn't
* "stick out" in the narrow part of the triangle.
* @return the size factor for a shape relatvie to its label
*/
public double getShapeToLabelRatio() {
return 1.0;
}
/**
* This is a factor to keep some shapes from being so distorted by very long labels that they
* effectively lose their shape when seen by the user
* @return the max width to height ratio
*/
public int getMaxWidthToHeightRatio() {
return 10;
}
protected abstract Shape createShape();
/**
* Returns the {@link VertexShape} for the given shape name
* @param shapeName the name of the shape for which to get the {@link VertexShape}
* @return the {@link VertexShape} for the given shape name
*/
public static VertexShape getShape(String shapeName) {
return registeredShapes.get(shapeName);
}
/**
* Returns a list of names for all the supported {@link VertexShape}s
* @return a list of names for all the supported {@link VertexShape}s
*/
public static List<String> getShapeNames() {
ArrayList<String> list = new ArrayList<String>(registeredShapes.keySet());
Collections.sort(list);
return list;
}
@Override
public int hashCode() {
return Objects.hash(name, size);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
VertexShape other = (VertexShape) obj;
return Objects.equals(name, other.name) && size == other.size;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//Vertex Shape Classes
//////////////////////////////////////////////////////////////////////////////////////////////////
static class RectangleVertexShape extends VertexShape {
private RectangleVertexShape(int size) {
super("Rectangle", size);
}
protected Shape createShape() {
return new Rectangle2D.Double(-1.0, -1.0, 2.0, 2.0);
}
}
static class EllipseVertexShape extends VertexShape {
private EllipseVertexShape(int size) {
super("Ellipse", size);
}
protected Shape createShape() {
return new Ellipse2D.Double(-1.0, 1.0, 2.0, 2.0);
}
@Override
public double getShapeToLabelRatio() {
return 1.4;
}
}
static class TriangleUpVertexShape extends VertexShape {
private TriangleUpVertexShape(int size) {
super("Triangle Up", size);
}
protected Shape createShape() {
Path2D path = new Path2D.Double();
path.moveTo(-1.0, 1.0);
path.lineTo(1.0, 1.0);
path.lineTo(0.0, -1.0);
path.closePath();
return path;
}
@Override
public double getShapeToLabelRatio() {
return 1.6;
}
@Override
public double getLabelPosition() {
return 0.90;
}
}
static class TriangleDownVertexShape extends VertexShape {
private TriangleDownVertexShape(int size) {
super("Triangle Down", size);
}
protected Shape createShape() {
Path2D path = new Path2D.Double();
path.moveTo(-1.0, -1.0);
path.lineTo(1.0, -1.0);
path.lineTo(0.0, 1.0);
path.closePath();
return path;
}
@Override
public double getShapeToLabelRatio() {
return 1.6;
}
@Override
public double getLabelPosition() {
return 0.10;
}
}
static class StarVertexShape extends VertexShape {
private StarVertexShape(int size) {
super("Star", size);
}
protected Shape createShape() {
int numPoints = 7;
Path2D path = new Path2D.Double();
double outerRadius = 2;
double innerRadius = 1;
double deltaAngle = Math.PI / numPoints;
double angle = 3 * Math.PI / 2; // start such that star points up.
path.moveTo(outerRadius * Math.cos(angle), outerRadius * Math.sin(angle));
for (int i = 0; i < numPoints; i++) {
angle += deltaAngle;
path.lineTo(innerRadius * Math.cos(angle), innerRadius * Math.sin(angle));
angle += deltaAngle;
path.lineTo(outerRadius * Math.cos(angle), outerRadius * Math.sin(angle));
}
return path;
}
@Override
public double getShapeToLabelRatio() {
return 2.0;
}
}
static class DiamondVertexShape extends VertexShape {
private DiamondVertexShape(int size) {
super("Diamond", size);
}
protected Shape createShape() {
Path2D path = new Path2D.Double();
path.moveTo(0.0, -1.0);
path.lineTo(-1.0, 0.0);
path.lineTo(0.0, 1.0);
path.lineTo(1.0, 0.0);
path.closePath();
return path;
}
@Override
public double getShapeToLabelRatio() {
return 1.6;
}
}
static class EquilateralPolygonVertexShape extends VertexShape {
private int numSides;
private double startAngle;
protected EquilateralPolygonVertexShape(String name, int numSides, double startAngle,
int size) {
super(name, size);
this.numSides = numSides;
this.startAngle = startAngle;
}
protected Shape createShape() {
Path2D path = new Path2D.Double();
double deltaAngle = Math.PI * 2 / numSides;
double angle = startAngle;
path.moveTo(Math.cos(angle), Math.sin(angle));
for (int i = 0; i < numSides; i++) {
angle += deltaAngle;
path.lineTo(Math.cos(angle), Math.sin(angle));
}
return path;
}
@Override
public int getMaxWidthToHeightRatio() {
return 2;
}
@Override
public double getShapeToLabelRatio() {
return 1.4;
}
}
static class PentagonVertexShape extends EquilateralPolygonVertexShape {
private PentagonVertexShape(int size) {
super("Pentaon", 5, Math.PI + Math.PI / 10, size);
}
}
static class HexagonVertexShape extends EquilateralPolygonVertexShape {
private HexagonVertexShape(int size) {
super("Hexagon", 6, 0, size);
}
}
static class OctagonVertexShape extends EquilateralPolygonVertexShape {
private OctagonVertexShape(int size) {
super("Octagon", 8, 0, size);
}
}
}

View file

@ -28,7 +28,7 @@ public class AttributedGraphTest {
@Before
public void setup() {
graph = new AttributedGraph();
graph = new AttributedGraph("Test", new EmptyGraphType());
}
@Test
@ -192,7 +192,7 @@ public class AttributedGraphTest {
@Test
public void testNonCollapsingEdges() {
graph = new AttributedGraph(false);
graph = new AttributedGraph("Test", new EmptyGraphType(), "Test", false);
AttributedVertex v1 = graph.addVertex("A");
AttributedVertex v2 = graph.addVertex("B");

View file

@ -0,0 +1,281 @@
/* ###
* 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.service.graph;
import static org.junit.Assert.*;
import java.awt.Color;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import docking.FakeDockingTool;
import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
import ghidra.util.HelpLocation;
import ghidra.util.WebColors;
public class GraphDisplayOptionsTest {
private GraphType graphType;
private GraphDisplayOptions options;
@Before
public void setUp() {
List<String> vertexTypes = Arrays.asList("V1", "V2", "V3" );
List<String> edgeTypes = Arrays.asList("E1", "E2", "E3" );
graphType = new GraphType("Test", "Test Description", vertexTypes, edgeTypes);
options = new GraphDisplayOptions(graphType);
}
@Test
public void testSetAndGetDefaultVertexShape() {
options.setDefaultVertexShape(VertexShape.STAR);
assertEquals(VertexShape.STAR, options.getDefaultVertexShape());
}
@Test
public void testSetAndGetDefaultVertexColor() {
options.setDefaultVertexColor(Color.MAGENTA);
assertEquals(Color.MAGENTA, options.getDefaultVertexColor());
}
@Test
public void testSetAndGetDefaultEdgeColor() {
options.setDefaultEdgeColor(Color.MAGENTA);
assertEquals(Color.MAGENTA, options.getDefaultEdgeColor());
}
@Test
public void testSetAndGetVertexLabelOverride() {
assertEquals(null, options.getVertexLabelOverride());
options.setVertexLabelOverrideAttributeKey("LABEL");
assertEquals("LABEL", options.getVertexLabelOverride());
}
@Test
public void testSetAndGetVertexColorOverrideAttributeKey() {
assertEquals(null, options.getVertexColorOverrideAttributeKey());
options.setVertexColorOverrideAttributeKey("COLOR");
assertEquals("COLOR", options.getVertexColorOverrideAttributeKey());
}
@Test
public void testSetAndGetEdgeColorOverrideAttributeKey() {
assertEquals(null, options.getEdgeColorOverrideAttributeKey());
options.setEdgeColorOverrideAttributeKey("COLOR");
assertEquals("COLOR", options.getEdgeColorOverrideAttributeKey());
}
@Test
public void testSetAndGetVertexShapeOverrideAttributeKey() {
assertEquals(null, options.getVertexShapeOverrideAttributeKey());
options.setVertexColorOverrideAttributeKey("SHAPE");
assertEquals("SHAPE", options.getVertexColorOverrideAttributeKey());
}
@Test
public void testGetVertexLabel() {
AttributedVertex vertex = new AttributedVertex("Foo");
assertEquals("Foo", options.getVertexLabel(vertex));
}
@Test
public void testGetVertexLabelWithLableOverride() {
options.setVertexLabelOverrideAttributeKey("Label");
AttributedVertex vertex = new AttributedVertex("Foo");
vertex.setAttribute("Label", "Bar");
assertEquals("Bar", options.getVertexLabel(vertex));
}
@Test
public void testGetVertexShape() {
options.setVertexShape("V1", VertexShape.DIAMOND);
options.setVertexShape("V2", VertexShape.PENTAGON);
AttributedVertex vertex = new AttributedVertex("Foo");
vertex.setVertexType("V1");
assertEquals(VertexShape.DIAMOND, options.getVertexShape(vertex));
vertex.setVertexType("V2");
assertEquals(VertexShape.PENTAGON, options.getVertexShape(vertex));
}
@Test
public void testGetVertexShapeWithOverride() {
options.setVertexShape("V1", VertexShape.DIAMOND);
options.setVertexShapeOverrideAttributeKey("Shape");
AttributedVertex vertex = new AttributedVertex("Foo");
vertex.setVertexType("V1");
assertEquals(VertexShape.DIAMOND, options.getVertexShape(vertex));
vertex.setAttribute("Shape", VertexShape.ELLIPSE.getName());
assertEquals(VertexShape.ELLIPSE, options.getVertexShape(vertex));
}
@Test
public void testGetVertexColor() {
options.setVertexColor("V1", Color.RED);
options.setVertexColor("V2", Color.GREEN);
AttributedVertex vertex = new AttributedVertex("Foo");
assertEquals(options.getDefaultVertexColor(), options.getVertexColor(vertex));
vertex.setVertexType("V1");
assertEquals(Color.RED, options.getVertexColor(vertex));
vertex.setVertexType("V2");
assertEquals(Color.GREEN, options.getVertexColor(vertex));
}
@Test
public void testGetVertexColorWithOverride() {
options.setVertexColor("V1", Color.RED);
options.setVertexColor("V2", Color.GREEN);
options.setVertexColorOverrideAttributeKey("Color");
AttributedVertex vertex = new AttributedVertex("Foo");
vertex.setVertexType("V1");
assertEquals(Color.RED, options.getVertexColor(vertex));
vertex.setAttribute("Color", WebColors.toString(Color.BLUE));
assertEquals(Color.BLUE, options.getVertexColor(vertex));
}
@Test
public void testGetEdgeColor() {
options.setEdgeColor("E1", Color.RED);
options.setEdgeColor("E2", Color.GREEN);
AttributedEdge edge = new AttributedEdge("1");
assertEquals(options.getDefaultEdgeColor(), options.getEdgeColor(edge));
edge.setEdgeType("E1");
assertEquals(Color.RED, options.getEdgeColor(edge));
edge.setEdgeType("E2");
assertEquals(Color.GREEN, options.getEdgeColor(edge));
}
@Test
public void testGetEdgeColorWithOverride() {
options.setEdgeColor("E1", Color.RED);
options.setEdgeColor("E2", Color.GREEN);
options.setEdgeColorOverrideAttributeKey("Color");
AttributedEdge edge = new AttributedEdge("1");
assertEquals(options.getDefaultEdgeColor(), options.getEdgeColor(edge));
edge.setEdgeType("E1");
assertEquals(Color.RED, options.getEdgeColor(edge));
edge.setAttribute("Color", WebColors.toString(Color.BLUE));
assertEquals(Color.BLUE, options.getEdgeColor(edge));
}
@Test
public void testGetEdgePriority() {
assertEquals(0, options.getEdgePriority("E1").intValue());
assertEquals(1, options.getEdgePriority("E2").intValue());
}
@Test
public void testGetFavoredEdgeType() {
// favored edge defaults to first edge defined
assertEquals("E1", options.getFavoredEdgeType());
options.setFavoredEdgeType("E2");
assertEquals("E2", options.getFavoredEdgeType());
}
@Test
public void testGetVertexColorForType() {
assertEquals(options.getDefaultVertexColor(), options.getVertexColor("V1"));
options.setVertexColor("V1", Color.RED);
assertEquals(Color.RED, options.getVertexColor("V1"));
}
@Test
public void testGetVertexShapeForType() {
assertEquals(options.getDefaultVertexShape(), options.getVertexShape("V1"));
options.setVertexShape("V1", VertexShape.STAR);
assertEquals(VertexShape.STAR, options.getVertexShape("V1"));
}
@Test
public void testGetEdgeColorForType() {
assertEquals(options.getDefaultEdgeColor(), options.getEdgeColor("V1"));
options.setEdgeColor("E1", Color.RED);
assertEquals(Color.RED, options.getEdgeColor("E1"));
}
@Test
public void testRegisterOptions() {
ToolOptions toolOptions = new ToolOptions("Test");
HelpLocation help = new HelpLocation("Topic", "anchor");
options.registerOptions(toolOptions, help);
Options graphDisplayOptions = toolOptions.getOptions(options.getRootOptionsName());
assertNotNull(graphDisplayOptions);
Options vertexColorOptions = graphDisplayOptions.getOptions("Vertex Colors");
List<String> leafOptionNames = vertexColorOptions.getLeafOptionNames();
assertEquals(Arrays.asList("V1", "V2", "V3"), leafOptionNames);
assertEquals(options.getDefaultVertexColor(),
vertexColorOptions.getColor("V1", Color.WHITE));
Options vertexShapeOptions = graphDisplayOptions.getOptions("Vertex Shapes");
leafOptionNames = vertexShapeOptions.getLeafOptionNames();
assertEquals(Arrays.asList("V1", "V2", "V3"), leafOptionNames);
assertEquals(options.getDefaultVertexShape().getName(),
vertexShapeOptions.getString("V1", "Bob"));
Options edgeColorOptions = graphDisplayOptions.getOptions("Edge Colors");
leafOptionNames = edgeColorOptions.getLeafOptionNames();
assertEquals(Arrays.asList("E1", "E2", "E3"), leafOptionNames);
assertEquals(options.getDefaultEdgeColor(),
edgeColorOptions.getColor("E1", Color.WHITE));
Options miscellaniousOptions = graphDisplayOptions.getOptions("Miscellanious");
leafOptionNames = miscellaniousOptions.getLeafOptionNames();
assertEquals(Arrays.asList("Use Icons", "Selected Vertex Color", "Default Layout Algorithm",
"Default Vertex Color", "Default Vertex Shape", "Selected Edge Color", "Label Position",
"Default Edge Color", "Font", "Favored Edge Type"), leafOptionNames);
}
@Test
public void testChangingToolOptionsAffectsGraph() {
FakeDockingTool tool = new FakeDockingTool();
ToolOptions toolOptions = tool.getOptions("Graph");
options.registerOptions(toolOptions, null);
options.initializeFromOptions(tool);
AttributedVertex vertex = new AttributedVertex("Foo");
vertex.setVertexType("V1");
assertEquals(Color.BLUE, options.getVertexColor(vertex));
Options graphDisplayOptions = toolOptions.getOptions(options.getRootOptionsName());
Options vertexColorOptions = graphDisplayOptions.getOptions("Vertex Colors");
vertexColorOptions.setColor("V1", Color.CYAN);
assertEquals(Color.CYAN, options.getVertexColor(vertex));
}
}

View file

@ -0,0 +1,71 @@
/* ###
* 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.service.graph;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
public class GraphTypeBuilderTest {
@Test
public void testName() {
GraphType graphType = new GraphTypeBuilder("Test").build();
assertEquals("Test", graphType.getName());
}
@Test
public void testDescription() {
GraphType graphType = new GraphTypeBuilder("Test")
.description("abc")
.build();
assertEquals("abc", graphType.getDescription());
}
@Test
public void testNoDescriptionUsesName() {
GraphType graphType = new GraphTypeBuilder("Test").build();
assertEquals("Test", graphType.getDescription());
}
@Test
public void testVertexType() {
GraphType graphType = new GraphTypeBuilder("Test")
.vertexType("V1")
.vertexType("V2")
.build();
List<String> vertexTypes = graphType.getVertexTypes();
assertEquals(2, vertexTypes.size());
assertEquals("V1", vertexTypes.get(0));
assertEquals("V2", vertexTypes.get(1));
}
@Test
public void testEdgeType() {
GraphType graphType = new GraphTypeBuilder("Test")
.edgeType("E1")
.edgeType("E2")
.build();
List<String> edgeTypes = graphType.getEdgeTypes();
assertEquals(2, edgeTypes.size());
assertEquals("E1", edgeTypes.get(0));
assertEquals("E2", edgeTypes.get(1));
}
}

View file

@ -0,0 +1,80 @@
/* ###
* 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.service.graph;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
public class GraphTypeTest {
private GraphType graphType;
@Before
public void setUp() {
List<String> vertexTypes = Arrays.asList("V1", "V2", "V3" );
List<String> edgeTypes = Arrays.asList("E1", "E2", "E3" );
graphType = new GraphType("Test", "Test Description", vertexTypes, edgeTypes);
}
@Test
public void testName() {
assertEquals("Test", graphType.getName());
}
@Test
public void testDescription() {
assertEquals("Test Description", graphType.getDescription());
}
@Test
public void testGetVertexTypes() {
List<String> types = graphType.getVertexTypes();
assertEquals(3, types.size());
assertEquals("V1", types.get(0));
assertEquals("V2", types.get(1));
assertEquals("V3", types.get(2));
}
@Test
public void testGetEdgeTypes() {
List<String> types = graphType.getEdgeTypes();
assertEquals(3, types.size());
assertEquals("E1", types.get(0));
assertEquals("E2", types.get(1));
assertEquals("E3", types.get(2));
}
@Test
public void testContainsVertexType() {
assertTrue(graphType.containsVertexType("V1"));
assertTrue(graphType.containsVertexType("V2"));
assertTrue(graphType.containsVertexType("V3"));
assertFalse(graphType.containsVertexType("E1"));
}
@Test
public void testContainsEdgeType() {
assertTrue(graphType.containsEdgeType("E1"));
assertTrue(graphType.containsEdgeType("E2"));
assertTrue(graphType.containsEdgeType("E3"));
assertFalse(graphType.containsEdgeType("V2"));
}
}