using CSharpCodeAnalyst.CodeGraph.Colors;
using CSharpCodeAnalyst.CodeGraph.Graph;
namespace CSharpCodeAnalyst.CodeGraph.Export;
public static class DgmlExport
{
///
/// Exports the given nodes and edges to a dgml file.
/// Note that the "Contains" relationship is treated as hierarchy information
/// to build sub-graphs in the output file.
///
public static void Export(string fileName, Graph.CodeGraph graph)
{
var builder = new DgmlFileBuilder();
WriteCategories(builder);
// Nodes and groups.
var containsRelationships = new List<(string sourceId, string targetId)>();
HashSet containers = [];
foreach (var node in graph.Nodes.Values)
{
if (node.Children.Any())
{
builder.AddGroup(node.Id, node.Name, node.ElementType.ToString());
containers.Add(node.Id);
}
else
{
builder.AddNodeById(node.Id, node.Name, node.ElementType.ToString());
}
containsRelationships.AddRange(node.Children.Select(c => (node.Id, c.Id)));
}
// Regular relationships
var normal = new List();
graph.ForEachNode(e => normal.AddRange(e.Relationships));
foreach (var edge in normal)
{
// Omit the calls label for better readability.
var edgeLabel = GetEdgeLabel(edge);
builder.AddEdgeById(edge.SourceId, edge.TargetId, edgeLabel);
}
// Containment relationships
foreach (var edge in containsRelationships)
{
if (containers.Contains(edge.targetId))
{
builder.AddGroupToGroup(edge.sourceId, edge.targetId);
}
else
{
builder.AddNodeToGroup(edge.sourceId, edge.targetId);
}
}
builder.WriteOutput(fileName);
}
private static string GetEdgeLabel(Relationship relationship)
{
// Omit the label text for now. The color makes it clear that it is a call relationship
if (relationship.Type == RelationshipType.Calls || relationship.Type == RelationshipType.Invokes)
{
return string.Empty;
}
// We can see this by the dotted line
if (relationship.Type == RelationshipType.Implements || relationship.Type == RelationshipType.Inherits)
{
return string.Empty;
}
if (relationship.Type == RelationshipType.Uses)
{
return string.Empty;
}
if (relationship.Type == RelationshipType.UsesAttribute)
{
return string.Empty;
}
return relationship.Type.ToString();
}
private static void WriteCategories(DgmlFileBuilder writer)
{
var elementTypes = Enum.GetValues(typeof(CodeElementType)).Cast();
foreach (var type in elementTypes)
{
writer.AddCategory(type.ToString(), "Background", $"#{ColorDefinitions.GetRbgOf(type):X}");
}
}
}