diff --git a/LICENSE.txt b/LICENSE.txt index 97a5e15..6b5e4ea 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2020 Valentin Fritz (aka. VFRZ) +Copyright (c) 2019-2023 Valentin Fritz (aka. VFRZ) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index c2a0a56..9df852b 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,99 @@ # DotNetGraph -Create **GraphViz DOT graph** with **.NET** +![Logo](Resources/icon_64.png) + +Create **GraphViz DOT graph** with **dotnet**. Available on NuGet: [![#](https://img.shields.io/nuget/v/DotNetGraph.svg)](https://www.nuget.org/packages/DotNetGraph/) -Compatible with **.NET Standard 2.0** and higher +Compatible with **.NET Standard 2.0** and higher. -# Documentation +# Usage ## Create a graph (*DotGraph*) ```csharp -var graph = new DotGraph("MyGraph"); +var graph = new DotGraph().WithIdentifier("MyGraph"); -var directedGraph = new DotGraph("MyDirectedGraph", true); +var directedGraph = new DotGraph().WithIdentifier("MyDirectedGraph").Directed(); ``` ## Create and add a node (*DotNode*) ```csharp -var myNode = new DotNode("MyNode") -{ - Shape = DotNodeShape.Ellipse, - Label = "My node!", - FillColor = Color.Coral, - FontColor = Color.Black, - Style = DotNodeStyle.Dotted, - Width = 0.5f, - Height = 0.5f, - PenWidth = 1.5f -}; +var myNode = new DotNode() + .WithIdentifier("MyNode") + .WithShape(DotNodeShape.Ellipse) + .WithLabel("My node!") + .WithFillColor(Color.Coral) + .WithFontColor(Color.Black) + .WithStyle(DotNodeStyle.Dotted) + .WithWidth(0.5) + .WithHeight(0.5) + .WithPenWidth(1.5); // Add the node to the graph -graph.Elements.Add(myNode); +graph.Add(myNode); ``` ## Create and add an edge (*DotEdge*) ```csharp // Create an edge with identifiers -var myEdge = new DotEdge("myNode1", "myNode2"); - -// Create an edge with nodes and attributes -var myEdge = new DotEdge(myNode1, myNode2) -{ - ArrowHead = DotEdgeArrowType.Box, - ArrowTail = DotEdgeArrowType.Diamond, - Color = Color.Red, - FontColor = Color.Black, - Label = "My edge!", - Style = DotEdgeStyle.Dashed, - PenWidth = 1.5f -}; +var myEdge = new DotEdge().From("Node1").To("Node2"); + +// Or with nodes and attributes +var myEdge = new DotEdge() + .From(node1) + .To(node2) + .WithArrowHead(DotEdgeArrowType.Box) + .WithArrowTail(DotEdgeArrowType.Diamond) + .WithColor(Color.Red) + .WithFontColor(Color.Black) + .WithLabel("My edge!") + .WithStyle(DotEdgeStyle.Dashed) + .WithPenWidth(1.5); // Add the edge to the graph -graph.Elements.Add(myEdge); +graph.Add(myEdge); ``` ## Create a subgraph / cluster ```csharp // Subgraph identifier need to start with "cluster" to be identified as a cluster -var mySubGraph = new DotSubGraph("cluster_0"); +var mySubGraph = new DotSubGraph().WithIdentifier("cluster_0"); // Create a subgraph with attributes (only used for cluster) -var mySubGraph = new DotSubGraph("cluster_0") -{ - Color = Color.Red, - Style = DotSubGraphStyle.Dashed, - Label = "My subgraph!" -}; +var mySubGraph = new DotSubGraph() + .WithIdentifier("cluster_0") + .WithColor(Color.Red) + .WithStyle(DotSubGraphStyle.Dashed) + .WithLabel("My subgraph!"); // Add node, edge, subgraph -subGraph.Elements.Add(myNode); -subGraph.Elements.Add(myEdge); -subGraph.Elements.Add(mySubGraph2); +subGraph.Add(myNode); +subGraph.Add(myEdge); +subGraph.Add(mySubGraph2); // Add subgraph to main graph -graph.Elements.Add(mySubGraph); +graph.Add(mySubGraph); ``` ## Compile to DOT format ```csharp -// Non indented version -var dot = graph.Compile(); -// Indented version -var dot = graph.Compile(true); +await using var writer = new StringWriter(); +var context = new CompilationContext(writer, new CompilationOptions()); +await graph.CompileAsync(context); + +var result = writer.GetStringBuilder().ToString(); // Save it to a file -File.WriteAllText("myFile.dot", dot); -``` \ No newline at end of file +File.WriteAllText("graph.dot", result); +``` +
+ +### Credits + +Logo: https://www.flaticon.com/free-icon/flow-chart_4411911 \ No newline at end of file diff --git a/Resources/icon_128.png b/Resources/icon_128.png new file mode 100644 index 0000000..7ead694 Binary files /dev/null and b/Resources/icon_128.png differ diff --git a/Resources/icon_64.png b/Resources/icon_64.png new file mode 100644 index 0000000..7f6c543 Binary files /dev/null and b/Resources/icon_64.png differ diff --git a/Sources/DotNetGraph.Tests/Attributes/DotAttributeTests.cs b/Sources/DotNetGraph.Tests/Attributes/DotAttributeTests.cs new file mode 100644 index 0000000..0719c0d --- /dev/null +++ b/Sources/DotNetGraph.Tests/Attributes/DotAttributeTests.cs @@ -0,0 +1,25 @@ +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Attributes; + +[TestClass] +public class DotAttributeTests +{ + [TestMethod] + public async Task Compile() + { + var attribute = new DotAttribute("testing"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("testing"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Attributes/DotColorAttributeTests.cs b/Sources/DotNetGraph.Tests/Attributes/DotColorAttributeTests.cs new file mode 100644 index 0000000..2f9403c --- /dev/null +++ b/Sources/DotNetGraph.Tests/Attributes/DotColorAttributeTests.cs @@ -0,0 +1,53 @@ +using System.Drawing; +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Attributes; + +[TestClass] +public class DotColorAttributeTests +{ + [TestMethod] + public async Task CompileFromString() + { + var attribute = new DotColorAttribute("red"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"red\""); + } + + [TestMethod] + public async Task CompileFromColor() + { + var attribute = new DotColorAttribute(Color.Red); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"#FF0000\""); + } + + [TestMethod] + public void ImplicitConversionFromColor() + { + DotColorAttribute attribute = Color.Red; + attribute.Value.Should().Be("#FF0000"); + } + + [TestMethod] + public void ImplicitConversionFromString() + { + DotColorAttribute attribute = "#FF0000"; + attribute.Value.Should().Be("#FF0000"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Attributes/DotDoubleAttributeTests.cs b/Sources/DotNetGraph.Tests/Attributes/DotDoubleAttributeTests.cs new file mode 100644 index 0000000..9cb1d54 --- /dev/null +++ b/Sources/DotNetGraph.Tests/Attributes/DotDoubleAttributeTests.cs @@ -0,0 +1,45 @@ +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Attributes; + +[TestClass] +public class DotDoubleAttributeTests +{ + [TestMethod] + public async Task CompileWithDefaultFormat() + { + var attribute = new DotDoubleAttribute(123.456); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("123.46"); + } + + [TestMethod] + public async Task CompileWithSpecifiedFormat() + { + var attribute = new DotDoubleAttribute(123.456, "F3"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("123.456"); + } + + [TestMethod] + public void ImplicitConversionFromDouble() + { + DotDoubleAttribute attribute = 123.456d; + attribute.Value.Should().Be(123.456d); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Attributes/DotEdgeArrowTypeAttributeTests.cs b/Sources/DotNetGraph.Tests/Attributes/DotEdgeArrowTypeAttributeTests.cs new file mode 100644 index 0000000..d8e6ddd --- /dev/null +++ b/Sources/DotNetGraph.Tests/Attributes/DotEdgeArrowTypeAttributeTests.cs @@ -0,0 +1,53 @@ +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; +using DotNetGraph.Core; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Attributes; + +[TestClass] +public class DotEdgeArrowTypeAttributeTests +{ + [TestMethod] + public async Task CompileFromString() + { + var attribute = new DotEdgeArrowTypeAttribute("custom"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"custom\""); + } + + [TestMethod] + public async Task CompileFromEnum() + { + var attribute = new DotEdgeArrowTypeAttribute(DotEdgeArrowType.Box); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"box\""); + } + + [TestMethod] + public void ImplicitConversionFromDotEdgeArrowType() + { + DotEdgeArrowTypeAttribute attribute = DotEdgeArrowType.Box; + attribute.Value.Should().Be("box"); + } + + [TestMethod] + public void ImplicitConversionFromString() + { + DotEdgeArrowTypeAttribute attribute = "box"; + attribute.Value.Should().Be("box"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Attributes/DotEdgeStyleAttributeTests.cs b/Sources/DotNetGraph.Tests/Attributes/DotEdgeStyleAttributeTests.cs new file mode 100644 index 0000000..f6d88b7 --- /dev/null +++ b/Sources/DotNetGraph.Tests/Attributes/DotEdgeStyleAttributeTests.cs @@ -0,0 +1,53 @@ +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; +using DotNetGraph.Core; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Attributes; + +[TestClass] +public class DotEdgeStyleAttributeTests +{ + [TestMethod] + public async Task CompileFromString() + { + var attribute = new DotEdgeStyleAttribute("custom"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"custom\""); + } + + [TestMethod] + public async Task CompileFromEnum() + { + var attribute = new DotEdgeStyleAttribute(DotEdgeStyle.Solid); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"solid\""); + } + + [TestMethod] + public void ImplicitConversionFromDotEdgeStyle() + { + DotEdgeStyleAttribute attribute = DotEdgeStyle.Solid; + attribute.Value.Should().Be("solid"); + } + + [TestMethod] + public void ImplicitConversionFromString() + { + DotEdgeStyleAttribute attribute = "solid"; + attribute.Value.Should().Be("solid"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Attributes/DotLabelAttributeTests.cs b/Sources/DotNetGraph.Tests/Attributes/DotLabelAttributeTests.cs new file mode 100644 index 0000000..35b3fea --- /dev/null +++ b/Sources/DotNetGraph.Tests/Attributes/DotLabelAttributeTests.cs @@ -0,0 +1,63 @@ +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Attributes; + +[TestClass] +public class DotLabelAttributeTests +{ + [TestMethod] + public async Task CompileDefault() + { + var attribute = new DotLabelAttribute("Hello,\r\n \"world\"!"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"Hello,\\n \\\"world\\\"!\""); + } + + [TestMethod] + public async Task CompileWithoutAutomaticEscapedCharactersFormat() + { + var attribute = new DotLabelAttribute("Hello,\r\n \"world\"!"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions + { + AutomaticEscapedCharactersFormat = false + }); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"Hello,\r\n \"world\"!\""); + } + + [TestMethod] + public async Task CompileHtml() + { + var attribute = new DotLabelAttribute("Hello, world!", true); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("<Hello, world!>"); + } + + [TestMethod] + public void ImplicitConversionFromString() + { + DotLabelAttribute attribute = "Hello, world!"; + + attribute.Value.Should().Be("Hello, world!"); + attribute.IsHtml.Should().Be(false); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Attributes/DotNodeShapeAttributeTests.cs b/Sources/DotNetGraph.Tests/Attributes/DotNodeShapeAttributeTests.cs new file mode 100644 index 0000000..2195e68 --- /dev/null +++ b/Sources/DotNetGraph.Tests/Attributes/DotNodeShapeAttributeTests.cs @@ -0,0 +1,53 @@ +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; +using DotNetGraph.Core; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Attributes; + +[TestClass] +public class DotNodeShapeAttributeTests +{ + [TestMethod] + public async Task CompileFromString() + { + var attribute = new DotNodeShapeAttribute("custom"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"custom\""); + } + + [TestMethod] + public async Task CompileFromEnum() + { + var attribute = new DotNodeShapeAttribute(DotNodeShape.Terminator); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"terminator\""); + } + + [TestMethod] + public void ImplicitConversionFromDotNodeShape() + { + DotNodeShapeAttribute attribute = DotNodeShape.Terminator; + attribute.Value.Should().Be("terminator"); + } + + [TestMethod] + public void ImplicitConversionFromString() + { + DotNodeShapeAttribute attribute = "terminator"; + attribute.Value.Should().Be("terminator"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Attributes/DotNodeStyleAttributeTests.cs b/Sources/DotNetGraph.Tests/Attributes/DotNodeStyleAttributeTests.cs new file mode 100644 index 0000000..faad002 --- /dev/null +++ b/Sources/DotNetGraph.Tests/Attributes/DotNodeStyleAttributeTests.cs @@ -0,0 +1,53 @@ +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; +using DotNetGraph.Core; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Attributes; + +[TestClass] +public class DotNodeStyleAttributeTests +{ + [TestMethod] + public async Task CompileFromString() + { + var attribute = new DotNodeStyleAttribute("custom"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"custom\""); + } + + [TestMethod] + public async Task CompileFromEnum() + { + var attribute = new DotNodeStyleAttribute(DotNodeStyle.Bold); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"bold\""); + } + + [TestMethod] + public void ImplicitConversionFromDotNodeStyle() + { + DotNodeStyleAttribute attribute = DotNodeStyle.Bold; + attribute.Value.Should().Be("bold"); + } + + [TestMethod] + public void ImplicitConversionFromString() + { + DotNodeStyleAttribute attribute = "bold"; + attribute.Value.Should().Be("bold"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Attributes/DotPointAttributeTests.cs b/Sources/DotNetGraph.Tests/Attributes/DotPointAttributeTests.cs new file mode 100644 index 0000000..deaa6ce --- /dev/null +++ b/Sources/DotNetGraph.Tests/Attributes/DotPointAttributeTests.cs @@ -0,0 +1,54 @@ +using System.Drawing; +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; +using DotNetGraph.Core; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Attributes; + +[TestClass] +public class DotPointAttributeTests +{ + [TestMethod] + public async Task CompileFromString() + { + var attribute = new DotPointAttribute("66,99"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("66,99"); + } + + [TestMethod] + public async Task CompileFromDotPoint() + { + var attribute = new DotPointAttribute(new DotPoint(42, 69, 75, true)); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("42,69,75!"); + } + + [TestMethod] + public void ImplicitConversionFromDotPoint() + { + DotPointAttribute attribute = new DotPoint(42, 69, 75, true); + attribute.Value.Should().Be("42,69,75!"); + } + + [TestMethod] + public void ImplicitConversionFromString() + { + DotPointAttribute attribute = "42,69,75!"; + attribute.Value.Should().Be("42,69,75!"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Attributes/DotSubgraphStyleAttributeTests.cs b/Sources/DotNetGraph.Tests/Attributes/DotSubgraphStyleAttributeTests.cs new file mode 100644 index 0000000..9dde479 --- /dev/null +++ b/Sources/DotNetGraph.Tests/Attributes/DotSubgraphStyleAttributeTests.cs @@ -0,0 +1,53 @@ +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; +using DotNetGraph.Core; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Attributes; + +[TestClass] +public class DotSubgraphStyleAttributeTests +{ + [TestMethod] + public async Task CompileFromString() + { + var attribute = new DotSubgraphStyleAttribute("custom"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"custom\""); + } + + [TestMethod] + public async Task CompileFromEnum() + { + var attribute = new DotSubgraphStyleAttribute(DotSubgraphStyle.Rounded); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await attribute.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"rounded\""); + } + + [TestMethod] + public void ImplicitConversionFromDotSubgraphStyle() + { + DotSubgraphStyleAttribute attribute = DotSubgraphStyle.Rounded; + attribute.Value.Should().Be("rounded"); + } + + [TestMethod] + public void ImplicitConversionFromString() + { + DotSubgraphStyleAttribute attribute = "rounded"; + attribute.Value.Should().Be("rounded"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/BasicGraphTests.cs b/Sources/DotNetGraph.Tests/BasicGraphTests.cs deleted file mode 100644 index 84ceac7..0000000 --- a/Sources/DotNetGraph.Tests/BasicGraphTests.cs +++ /dev/null @@ -1,84 +0,0 @@ -using DotNetGraph.Extensions; -using DotNetGraph.Node; -using NFluent; -using Xunit; - -namespace DotNetGraph.Tests -{ - public class BasicGraphTests - { - [Fact] - public void GraphWithSpaceInIdentifier() - { - var graph = new DotGraph("My test graph"); - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph \"My test graph\" { }"); - } - - [Fact] - public void EmptyDirectedGraph() - { - var graph = new DotGraph("TestGraph", true); - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("digraph TestGraph { }"); - } - - [Fact] - public void EmptyGraph() - { - var graph = new DotGraph("TestGraph"); - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { }"); - } - - [Fact] - public void EmptyStrictGraph() - { - var graph = new DotGraph("TestGraph") - { - Strict = true - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("strict graph TestGraph { }"); - } - - [Fact] - public void GraphWithoutStringsFormat() - { - var graph = new DotGraph("TestGraph", true); - - graph.Elements.Add(new DotNode("TestNode") - { - Label = "\\lTesting" - }); - - var compiled = graph.Compile(false, false); - - Check.That(compiled).HasSameValueAs("digraph TestGraph { TestNode[label=\"\\lTesting\"]; }"); - } - - [Fact] - public void DotGraph_WhenRawLineAdded_ThenItsCompiled() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotString("rankdir = TB;") - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { rankdir = TB; }"); - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Compilation/CompilationContextTests.cs b/Sources/DotNetGraph.Tests/Compilation/CompilationContextTests.cs new file mode 100644 index 0000000..5159974 --- /dev/null +++ b/Sources/DotNetGraph.Tests/Compilation/CompilationContextTests.cs @@ -0,0 +1,87 @@ +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Compilation; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Compilation; + +[TestClass] +public class CompilationContextTests +{ + [TestMethod] + public async Task WriteIndentationAsyncIndentedLevel3() + { + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions + { + Indented = true + }) + { + IndentationLevel = 3 + }; + await context.WriteIndentationAsync(); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\t\t\t"); + } + + [TestMethod] + public async Task WriteIndentationAsyncNonIndentedLevel3() + { + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions + { + Indented = false + }) + { + IndentationLevel = 3 + }; + await context.WriteIndentationAsync(); + + var result = writer.GetStringBuilder().ToString(); + result.Should().BeEmpty(); + } + + [TestMethod] + public async Task WriteAsync() + { + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + + await context.WriteAsync("Hello, world!"); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("Hello, world!"); + } + + [TestMethod] + public async Task WriteLineAsyncIndented() + { + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions + { + Indented = true + }); + + await context.WriteLineAsync("Hello, world!"); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("Hello, world!\n"); + } + + [TestMethod] + public async Task WriteLineAsyncNonIndented() + { + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions + { + Indented = false + }); + + await context.WriteLineAsync("Hello, world!"); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("Hello, world! "); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Core/DotEdgeTests.cs b/Sources/DotNetGraph.Tests/Core/DotEdgeTests.cs new file mode 100644 index 0000000..4896c5e --- /dev/null +++ b/Sources/DotNetGraph.Tests/Core/DotEdgeTests.cs @@ -0,0 +1,101 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Compilation; +using DotNetGraph.Core; +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Core; + +[TestClass] +public class DotEdgeTests +{ + [TestMethod] + public async Task CompileEmptyEdge() + { + var edge = new DotEdge() + .From("A") + .To("B"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await edge.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"A\" -- \"B\"\n"); + } + + [TestMethod] + public async Task CompileEmptyDirectedEdge() + { + var edge = new DotEdge() + .From("A") + .To("B"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()) + { + DirectedGraph = true + }; + await edge.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"A\" -> \"B\"\n"); + } + + [TestMethod] + public async Task CompileWithMissingFrom() + { + var edge = new DotEdge() + .To("B"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()) + { + DirectedGraph = true + }; + + await edge.Invoking(e => e.CompileAsync(context)) + .Should() + .ThrowAsync(); + } + + [TestMethod] + public async Task CompileWithMissingTo() + { + var edge = new DotEdge() + .From("A"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()) + { + DirectedGraph = true + }; + + await edge.Invoking(e => e.CompileAsync(context)) + .Should() + .ThrowAsync(); + } + + [TestMethod] + public async Task CompileWithAttributes() + { + var edge = new DotEdge() + .From("A") + .To("B") + .WithLabel("Test") + .WithStyle(DotEdgeStyle.Bold); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()) + { + DirectedGraph = true + }; + await edge.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"A\" -> \"B\" [\n\t\"label\"=\"Test\"\n\t\"style\"=\"bold\"\n]\n"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Core/DotElementTests.cs b/Sources/DotNetGraph.Tests/Core/DotElementTests.cs new file mode 100644 index 0000000..ba110cd --- /dev/null +++ b/Sources/DotNetGraph.Tests/Core/DotElementTests.cs @@ -0,0 +1,152 @@ +using System; +using DotNetGraph.Attributes; +using DotNetGraph.Core; +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Core; + +[TestClass] +public class DotElementTests +{ + [TestMethod] + public void HasAttributeTrue() + { + var node = new DotNode() + .WithLabel("Test"); + + var hasAttribute = node.HasAttribute("label"); + + hasAttribute.Should().BeTrue(); + } + + [TestMethod] + public void HasAttributeFalse() + { + var node = new DotNode(); + + var hasAttribute = node.HasAttribute("label"); + + hasAttribute.Should().BeFalse(); + } + + [TestMethod] + public void GetAttributeMissing() + { + var node = new DotNode(); + + node.Invoking(n => n.GetAttribute("label")) + .Should() + .Throw(); + } + + [TestMethod] + public void GetAttributeOrDefault() + { + var node = new DotNode() + .WithLabel("Test"); + + var attribute = node.GetAttributeOrDefault("label"); + + attribute.Should().NotBeNull(); + } + + [TestMethod] + public void GetAttributeOrDefaultMissing() + { + var node = new DotNode(); + + var attribute = node.GetAttributeOrDefault("label"); + + attribute.Should().BeNull(); + } + + [TestMethod] + public void GetAttributeGenericWrongType() + { + var node = new DotNode() + .WithLabel("Test"); + + node.Invoking(n => n.GetAttribute("label")) + .Should() + .Throw(); + } + + [TestMethod] + public void GetAttributeOrDefaultGenericWrongType() + { + var node = new DotNode() + .WithLabel("Test"); + + node.Invoking(n => n.GetAttributeOrDefault("label")) + .Should() + .Throw(); + } + + [TestMethod] + public void GetAttributeOrDefaultGeneric() + { + var node = new DotNode(); + + var attribute = node.GetAttributeOrDefault("label"); + + attribute.Should().BeNull(); + } + + [TestMethod] + public void SetAttributeToNull() + { + var node = new DotNode() + .WithLabel("Test"); + + node.SetAttribute("label", null); + + node.Label.Should().BeNull(); + } + + [TestMethod] + public void TryGetAttributeMissing() + { + var node = new DotNode(); + + var success = node.TryGetAttribute("label", out var attribute); + + success.Should().BeFalse(); + attribute.Should().BeNull(); + } + + [TestMethod] + public void TryGetAttributeGeneric() + { + var node = new DotNode() + .WithLabel("Test"); + + var success = node.TryGetAttribute("label", out var attribute); + + success.Should().BeTrue(); + attribute.Should().NotBeNull(); + } + + [TestMethod] + public void TryGetAttributeGenericMissing() + { + var node = new DotNode(); + + var success = node.TryGetAttribute("label", out var attribute); + + success.Should().BeFalse(); + attribute.Should().BeNull(); + } + + [TestMethod] + public void TryGetAttributeGenericWrongType() + { + var node = new DotNode() + .WithLabel("Test"); + + node.Invoking(n => node.TryGetAttribute("label", out _)) + .Should() + .Throw(); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Core/DotGraphTests.cs b/Sources/DotNetGraph.Tests/Core/DotGraphTests.cs new file mode 100644 index 0000000..9cbd341 --- /dev/null +++ b/Sources/DotNetGraph.Tests/Core/DotGraphTests.cs @@ -0,0 +1,73 @@ +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Compilation; +using DotNetGraph.Core; +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Core; + +[TestClass] +public class DotGraphTests +{ + [TestMethod] + public async Task CompileEmptyGraph() + { + var graph = new DotGraph() + .WithIdentifier("Test"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await graph.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("graph \"Test\" {\n}\n"); + } + + [TestMethod] + public async Task CompileEmptyStrictGraph() + { + var graph = new DotGraph() + .WithIdentifier("Test") + .Strict(); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await graph.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("strict graph \"Test\" {\n}\n"); + } + + [TestMethod] + public async Task CompileEmptyDirectedGraph() + { + var graph = new DotGraph() + .WithIdentifier("Test") + .Directed(); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await graph.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("digraph \"Test\" {\n}\n"); + } + + [TestMethod] + public async Task CompileEmptyDirectedGraphWithRankDir() + { + var graph = new DotGraph() + .WithIdentifier("Test") + .WithRankDir(DotRankDir.RL) + .Directed(); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await graph.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("digraph \"Test\" {\n\trankdir=\"RL\"\n}\n"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Core/DotIdentifierTests.cs b/Sources/DotNetGraph.Tests/Core/DotIdentifierTests.cs new file mode 100644 index 0000000..8c756ef --- /dev/null +++ b/Sources/DotNetGraph.Tests/Core/DotIdentifierTests.cs @@ -0,0 +1,38 @@ +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Compilation; +using DotNetGraph.Core; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Core; + +[TestClass] +public class DotIdentifierTests +{ + [TestMethod] + public async Task Compile() + { + var identifier = new DotIdentifier("Test"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await identifier.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"Test\""); + } + + [TestMethod] + public async Task CompileHtml() + { + var identifier = new DotIdentifier("Test", true); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await identifier.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("<Test>"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Core/DotNodeTests.cs b/Sources/DotNetGraph.Tests/Core/DotNodeTests.cs new file mode 100644 index 0000000..6a4bfec --- /dev/null +++ b/Sources/DotNetGraph.Tests/Core/DotNodeTests.cs @@ -0,0 +1,43 @@ +using System.Drawing; +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Compilation; +using DotNetGraph.Core; +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Core; + +[TestClass] +public class DotNodeTests +{ + [TestMethod] + public async Task CompileEmptyNode() + { + var node = new DotNode() + .WithIdentifier("Test"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await node.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"Test\"\n"); + } + + [TestMethod] + public async Task CompileNodeWithColor() + { + var node = new DotNode() + .WithIdentifier("Test") + .WithColor(Color.Red); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await node.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("\"Test\" [\n\t\"color\"=\"#FF0000\"\n]\n"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Core/DotSubgraphTests.cs b/Sources/DotNetGraph.Tests/Core/DotSubgraphTests.cs new file mode 100644 index 0000000..707e68b --- /dev/null +++ b/Sources/DotNetGraph.Tests/Core/DotSubgraphTests.cs @@ -0,0 +1,27 @@ +using System.IO; +using System.Threading.Tasks; +using DotNetGraph.Compilation; +using DotNetGraph.Core; +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Core; + +[TestClass] +public class DotSubgraphTests +{ + [TestMethod] + public async Task CompileEmptySubgraph() + { + var subgraph = new DotSubgraph() + .WithIdentifier("Test"); + + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await subgraph.CompileAsync(context); + + var result = writer.GetStringBuilder().ToString(); + result.Should().Be("subgraph \"Test\" {\n}\n"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/DotCompilerWorkerTests.cs b/Sources/DotNetGraph.Tests/DotCompilerWorkerTests.cs deleted file mode 100644 index 34b9127..0000000 --- a/Sources/DotNetGraph.Tests/DotCompilerWorkerTests.cs +++ /dev/null @@ -1,64 +0,0 @@ -using DotNetGraph.Compiler; -using NFluent; -using Xunit; - -namespace DotNetGraph.Tests -{ - public class DotCompilerWorkerTests - { - [Fact] - public void Format() - { - const string text = "Je m'appelle \"Jack\",\r\n je suis un test\\essai\nCela marche!"; - - var formatted = DotCompilerWorker.FormatString(text, true); - - Check.That(formatted).HasSameValueAs("Je m'appelle \\\"Jack\\\",\\n je suis un test\\\\essai\\nCela marche!"); - } - - [Fact] - public void Format_Disabled() - { - const string text = "Je m'appelle \"Jack\",\r\n je suis un test\\essai\nCela marche!"; - - var formatted = DotCompilerWorker.FormatString(text, false); - - Check.That(formatted).HasSameValueAs(text); - } - - [Theory] - [InlineData("node")] - [InlineData("node123")] - [InlineData("underscores_are_allowed")] - [InlineData("-123")] - [InlineData("123")] - [InlineData("1.23")] - [InlineData("-1.23")] - public void SurroundWithDoubleQuotes_Without(string text) - { - var formatted = DotCompilerWorker.SurroundStringWithQuotes(text, false); - - Check.That(formatted).HasSameValueAs(text); - } - - [Theory] - [InlineData("no[]de")] - [InlineData("no\"de")] - [InlineData("no\nde")] - [InlineData("123start_with_number")] - [InlineData("identifier with space")] - [InlineData("\"node\"")] - [InlineData("節点")] - [InlineData("узел")] - [InlineData("1a")] - [InlineData("-1a")] - [InlineData("1.1a")] - [InlineData("-1.1a")] - public void SurroundWithDoubleQuotes_With(string text) - { - var formatted = DotCompilerWorker.SurroundStringWithQuotes(text, false); - - Check.That(formatted).HasSameValueAs("\"" + text + "\""); - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/DotNetGraph.Tests.csproj b/Sources/DotNetGraph.Tests/DotNetGraph.Tests.csproj index db9c998..e7a061c 100644 --- a/Sources/DotNetGraph.Tests/DotNetGraph.Tests.csproj +++ b/Sources/DotNetGraph.Tests/DotNetGraph.Tests.csproj @@ -2,22 +2,17 @@ net6.0 - false + enable - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + - + diff --git a/Sources/DotNetGraph.Tests/Edge/BasicEdgeTests.cs b/Sources/DotNetGraph.Tests/Edge/BasicEdgeTests.cs deleted file mode 100644 index 7e9d6fb..0000000 --- a/Sources/DotNetGraph.Tests/Edge/BasicEdgeTests.cs +++ /dev/null @@ -1,308 +0,0 @@ -using System; -using System.Drawing; -using DotNetGraph.Core; -using DotNetGraph.Edge; -using DotNetGraph.Extensions; -using DotNetGraph.Node; -using NFluent; -using Xunit; - -namespace DotNetGraph.Tests.Edge -{ - public class BasicEdgeTests - { - [Fact] - public void EdgeWithIdentifierToIdentifier() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotEdge("hello", "world") - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { hello -- world; }"); - } - - [Fact] - public void EdgeWithIdentifierToIdentifierDirectedGraph() - { - var graph = new DotGraph("TestGraph") - { - Directed = true, - Elements = - { - new DotEdge("hello", "world") - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("digraph TestGraph { hello -> world; }"); - } - - [Fact] - public void EdgeWithNodeToNode() - { - var helloNode = new DotNode("hello"); - - var worldNode = new DotNode("world"); - - var graph = new DotGraph("TestGraph") - { - Elements = - { - helloNode, - worldNode, - new DotEdge(helloNode, worldNode) - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { hello; world; hello -- world; }"); - } - - [Fact] - public void EdgeWithMultipleAttributes() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotEdge("hello", "world") - { - Color = Color.Red, - ArrowHead = DotEdgeArrowType.Box, - ArrowTail = DotEdgeArrowType.Diamond - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { hello -- world[color=\"#FF0000\",arrowhead=box,arrowtail=diamond]; }"); - } - - [Fact] - public void EdgeWithColor() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotEdge("hello", "world") - { - Color = Color.Red - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { hello -- world[color=\"#FF0000\"]; }"); - } - - [Fact] - public void EdgeWithFontColor() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotEdge("hello", "world") - { - FontColor = Color.Blue - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { hello -- world[fontcolor=\"#0000FF\"]; }"); - } - - [Fact] - public void EdgeWithPosition() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotEdge("hello", "world") - { - Position = new DotPosition(4, 2) - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { hello -- world[pos=\"4,2!\"]; }"); - } - - [Fact] - public void EdgeWithPenWidth() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotEdge("hello", "world") - { - PenWidth = 0.46f - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { hello -- world[penwidth=0.46]; }"); - } - - [Fact] - public void EdgeWithLabel() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotEdge("hello", "world") - { - Label = "Hello, \"world\"!" - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { hello -- world[label=\"Hello, \\\"world\\\"!\"]; }"); - } - - [Fact] - public void EdgeWithStyle() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotEdge("hello", "world") - { - Style = DotEdgeStyle.Dashed - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { hello -- world[style=dashed]; }"); - } - - [Fact] - public void EdgeWithMultipleStyles() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotEdge("hello", "world") - { - Style = DotEdgeStyle.Dashed | DotEdgeStyle.Dotted - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { hello -- world[style=\"dashed,dotted\"]; }"); - } - - [Fact] - public void EdgeWithArrowHead() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotEdge("hello", "world") - { - ArrowHead = DotEdgeArrowType.Box - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { hello -- world[arrowhead=box]; }"); - } - - [Fact] - public void EdgeWithArrowTail() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotEdge("hello", "world") - { - ArrowTail = DotEdgeArrowType.Diamond - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { hello -- world[arrowtail=diamond]; }"); - } - - [Fact] - public void EdgeWithNullNodesThrowsException() - { - var node = new DotNode("example"); - - Check.ThatCode(() => new DotEdge(null, node)).Throws(); - Check.ThatCode(() => new DotEdge(node, null)).Throws(); - - Check.ThatCode(() => new DotEdge(null, "test")).Throws(); - Check.ThatCode(() => new DotEdge("test", null)).Throws(); - } - - [Fact] - public void EdgeWithEmptyNodeIdentifierThrowsException() - { - Check.ThatCode(() => new DotEdge(string.Empty, "test")).Throws(); - Check.ThatCode(() => new DotEdge(" ", "test")).Throws(); - Check.ThatCode(() => new DotEdge("test", string.Empty)).Throws(); - Check.ThatCode(() => new DotEdge("test", " ")).Throws(); - } - - [Fact] - public void ModifyEdgeWithNullNodesThrowsException() - { - var edge = new DotEdge(new DotNode("left"), new DotNode("right")); - - Check.ThatCode(() => edge.Left = null).Throws(); - Check.ThatCode(() => edge.Right = null).Throws(); - } - - [Theory] - [InlineData("style")] - [InlineData("Style")] - [InlineData("STYLE")] - public void DotEdge_WhenCustomAttributeSet_ThenItsCompiled(string styleName) - { - var graph = new DotGraph("TestGraph") - .AddEdge("hello", "world", e => - { - e.SetCustomAttribute(styleName, "dashed"); - }); - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { hello -- world[style=dashed]; }"); - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Extensions/ColorExtensionsTests.cs b/Sources/DotNetGraph.Tests/Extensions/ColorExtensionsTests.cs new file mode 100644 index 0000000..45b9a1b --- /dev/null +++ b/Sources/DotNetGraph.Tests/Extensions/ColorExtensionsTests.cs @@ -0,0 +1,30 @@ +using System.Drawing; +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Extensions; + +[TestClass] +public class ColorExtensionsTests +{ + [TestMethod] + public void ToHexStringRgb() + { + var color = Color.FromArgb(123, 123, 255); + + var hex = color.ToHexString(); + + hex.Should().Be("#7B7BFF"); + } + + [TestMethod] + public void ToHexStringRgba() + { + var color = Color.FromArgb(123, 123, 123, 255); + + var hex = color.ToHexString(); + + hex.Should().Be("#7B7BFF7B"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Extensions/DotBaseGraphExtensionsTests.cs b/Sources/DotNetGraph.Tests/Extensions/DotBaseGraphExtensionsTests.cs new file mode 100644 index 0000000..3422c6b --- /dev/null +++ b/Sources/DotNetGraph.Tests/Extensions/DotBaseGraphExtensionsTests.cs @@ -0,0 +1,50 @@ +using DotNetGraph.Core; +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Extensions; + +[TestClass] +public class DotBaseGraphExtensionsTests +{ + [TestMethod] + public void WithIdentifier() + { + var graph = new DotGraph() + .WithIdentifier("Test"); + + graph.Identifier.Value.Should().Be("Test"); + graph.Identifier.IsHtml.Should().Be(false); + } + + [TestMethod] + public void WithIdentifierHtml() + { + var graph = new DotGraph() + .WithIdentifier("Test", true); + + graph.Identifier.Value.Should().Be("Test"); + graph.Identifier.IsHtml.Should().Be(true); + } + + [TestMethod] + public void WithRankDir() + { + var graph = new DotGraph() + .WithRankDir(DotRankDir.TB); + + graph.RankDir.Should().Be(DotRankDir.TB); + } + + [TestMethod] + public void Add() + { + var node = new DotNode(); + + var graph = new DotGraph() + .Add(node); + + graph.Elements.Should().Contain(node); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Extensions/DotEdgeExtensionsTests.cs b/Sources/DotNetGraph.Tests/Extensions/DotEdgeExtensionsTests.cs new file mode 100644 index 0000000..b787a06 --- /dev/null +++ b/Sources/DotNetGraph.Tests/Extensions/DotEdgeExtensionsTests.cs @@ -0,0 +1,211 @@ +using System.Drawing; +using DotNetGraph.Core; +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Extensions; + +[TestClass] +public class DotEdgeExtensionsTests +{ + [TestMethod] + public void FromStringDefault() + { + var edge = new DotEdge() + .From("a"); + + edge.From.Value.Should().Be("a"); + edge.From.IsHtml.Should().Be(false); + } + + [TestMethod] + public void FromStringHtml() + { + var edge = new DotEdge() + .From("a", true); + + edge.From.Value.Should().Be("a"); + edge.From.IsHtml.Should().Be(true); + } + + [TestMethod] + public void FromNode() + { + var node = new DotNode() + .WithIdentifier("a"); + + var edge = new DotEdge() + .From(node); + + edge.From.Value.Should().Be("a"); + edge.From.IsHtml.Should().Be(false); + } + + [TestMethod] + public void FromNodeHtml() + { + var node = new DotNode() + .WithIdentifier("a", true); + + var edge = new DotEdge() + .From(node); + + edge.From.Value.Should().Be("a"); + edge.From.IsHtml.Should().Be(true); + } + + [TestMethod] + public void ToStringDefault() + { + var edge = new DotEdge() + .To("a"); + + edge.To.Value.Should().Be("a"); + edge.To.IsHtml.Should().Be(false); + } + + [TestMethod] + public void ToStringHtml() + { + var edge = new DotEdge() + .To("a", true); + + edge.To.Value.Should().Be("a"); + edge.To.IsHtml.Should().Be(true); + } + + [TestMethod] + public void ToNode() + { + var node = new DotNode() + .WithIdentifier("a"); + + var edge = new DotEdge() + .To(node); + + edge.To.Value.Should().Be("a"); + edge.To.IsHtml.Should().Be(false); + } + + [TestMethod] + public void ToNodeHtml() + { + var node = new DotNode() + .WithIdentifier("a", true); + + var edge = new DotEdge() + .To(node); + + edge.To.Value.Should().Be("a"); + edge.To.IsHtml.Should().Be(true); + } + + [TestMethod] + public void WithColorString() + { + var edge = new DotEdge() + .WithColor("red"); + + edge.Color.Value.Should().Be("red"); + } + + [TestMethod] + public void WithColor() + { + var edge = new DotEdge() + .WithColor(Color.Red); + + edge.Color.Value.Should().Be("#FF0000"); + } + + [TestMethod] + public void WithStyleString() + { + var edge = new DotEdge() + .WithStyle("custom"); + + edge.Style.Value.Should().Be("custom"); + } + + [TestMethod] + public void WithStyle() + { + var edge = new DotEdge() + .WithStyle(DotEdgeStyle.Dashed); + + edge.Style.Value.Should().Be("dashed"); + } + + [TestMethod] + public void WithPenWidth() + { + var edge = new DotEdge() + .WithPenWidth(123.456); + + edge.PenWidth.Value.Should().Be(123.456); + } + + [TestMethod] + public void WithArrowHeadString() + { + var edge = new DotEdge() + .WithArrowHead("custom"); + + edge.ArrowHead.Value.Should().Be("custom"); + } + + [TestMethod] + public void WithArrowHead() + { + var edge = new DotEdge() + .WithArrowHead(DotEdgeArrowType.Diamond); + + edge.ArrowHead.Value.Should().Be("diamond"); + } + + [TestMethod] + public void WithArrowTailString() + { + var edge = new DotEdge() + .WithArrowTail("custom"); + + edge.ArrowTail.Value.Should().Be("custom"); + } + + [TestMethod] + public void WithArrowTail() + { + var edge = new DotEdge() + .WithArrowTail(DotEdgeArrowType.Diamond); + + edge.ArrowTail.Value.Should().Be("diamond"); + } + + [TestMethod] + public void WithPosString() + { + var edge = new DotEdge() + .WithPos("42,69"); + + edge.Pos.Value.Should().Be("42,69"); + } + + [TestMethod] + public void WithPos2D() + { + var edge = new DotEdge() + .WithPos(42, 69); + + edge.Pos.Value.Should().Be("42,69"); + } + + [TestMethod] + public void WithPos3D() + { + var edge = new DotEdge() + .WithPos(42, 69, 75); + + edge.Pos.Value.Should().Be("42,69,75"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Extensions/DotElementExtensionsTests.cs b/Sources/DotNetGraph.Tests/Extensions/DotElementExtensionsTests.cs new file mode 100644 index 0000000..da4e3cf --- /dev/null +++ b/Sources/DotNetGraph.Tests/Extensions/DotElementExtensionsTests.cs @@ -0,0 +1,60 @@ +using System.Drawing; +using DotNetGraph.Attributes; +using DotNetGraph.Core; +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Extensions; + +[TestClass] +public class DotElementExtensionsTests +{ + [TestMethod] + public void WithLabel() + { + var node = new DotNode() + .WithLabel("Test"); + + node.Label.Value.Should().Be("Test"); + node.Label.IsHtml.Should().Be(false); + } + + [TestMethod] + public void WithAttributeString() + { + var node = new DotNode() + .WithAttribute("hello", "world"); + + var attribute = node.GetAttribute("hello") as DotAttribute; + attribute?.Value.Should().Be("world"); + } + + [TestMethod] + public void WithAttribute() + { + var node = new DotNode() + .WithAttribute("hello", new DotAttribute("world")); + + var attribute = node.GetAttribute("hello") as DotAttribute; + attribute?.Value.Should().Be("world"); + } + + [TestMethod] + public void WithFontColorString() + { + var node = new DotNode() + .WithFontColor("red"); + + node.FontColor.Value.Should().Be("red"); + } + + [TestMethod] + public void WithFontColor() + { + var node = new DotNode() + .WithFontColor(Color.Red); + + node.FontColor.Value.Should().Be("#FF0000"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Extensions/DotGraphExtensionsTests.cs b/Sources/DotNetGraph.Tests/Extensions/DotGraphExtensionsTests.cs index 5388e03..f54570c 100644 --- a/Sources/DotNetGraph.Tests/Extensions/DotGraphExtensionsTests.cs +++ b/Sources/DotNetGraph.Tests/Extensions/DotGraphExtensionsTests.cs @@ -1,80 +1,28 @@ -using DotNetGraph.SubGraph; -using DotNetGraph.Extensions; -using System.Collections.Generic; -using Xunit; -using NFluent; using DotNetGraph.Core; -using DotNetGraph.Node; -using DotNetGraph.Edge; +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace DotNetGraph.Tests.Extensions +namespace DotNetGraph.Tests.Extensions; + +[TestClass] +public class DotGraphExtensionsTests { - public class DotGraphExtensionsTests + [TestMethod] + public void Directed() { - public static IEnumerable GetGraphs() - { - yield return new[] { new DotGraph("G") }; - yield return new[] { new DotSubGraph("cluster_0") }; - } - - private void AssertSingleElement(IDotGraph sut) - where T : IDotElement - { - Check.That(sut.Elements).CountIs(1); - Check.That(sut.Elements[0]).IsInstanceOfType(typeof(T)); - } - - [Theory] - [MemberData(nameof(GetGraphs))] - public void AddNode_WhenCalled_ThenNewNodeIsAdded(IDotGraph sut) - { - var id = "A"; - sut.AddNode(id); - - AssertSingleElement(sut); - } - - [Theory] - [MemberData(nameof(GetGraphs))] - public void AddEdge_WhenCalledWithStrings_ThenNewEdgeIsAdded(IDotGraph sut) - { - var left = "A"; - var right = "B"; - sut.AddEdge(left, right); + var graph = new DotGraph() + .Directed(); - AssertSingleElement(sut); - } - - [Theory] - [MemberData(nameof(GetGraphs))] - public void AddEdge_WhenCalledWithElements_ThenNewEdgeIsAdded(IDotGraph sut) - { - var left = new DotNode("A"); - var right = new DotNode("B"); - sut.AddEdge(left, right); - - AssertSingleElement(sut); - } - - [Theory] - [MemberData(nameof(GetGraphs))] - public void AddSubGraph_WhenCalled_ThenSubGraphIsAdded(IDotGraph sut) - { - var id = "A"; - sut.AddSubGraph(id); - - AssertSingleElement(sut); - } - - [Theory] - [MemberData(nameof(GetGraphs))] - public void AddLine_WhenCalled_ThenLineIsAdded(IDotGraph sut) - { - var line = "raw line"; - sut.AddLine(line); + graph.Directed.Should().Be(true); + } + + [TestMethod] + public void Strict() + { + var graph = new DotGraph() + .Strict(); - Check.That(sut.Elements).CountIs(1); - Check.That(((DotString)sut.Elements[0]).Value).HasSameValueAs(line); - } + graph.Strict.Should().Be(true); } -} +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Extensions/DotNodeExtensionsTests.cs b/Sources/DotNetGraph.Tests/Extensions/DotNodeExtensionsTests.cs new file mode 100644 index 0000000..bdb5b1c --- /dev/null +++ b/Sources/DotNetGraph.Tests/Extensions/DotNodeExtensionsTests.cs @@ -0,0 +1,157 @@ +using System.Drawing; +using DotNetGraph.Core; +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Extensions; + +[TestClass] +public class DotNodeExtensionsTests +{ + [TestMethod] + public void WithIdentifier() + { + var node = new DotNode() + .WithIdentifier("Test"); + + node.Identifier.Value.Should().Be("Test"); + node.Identifier.IsHtml.Should().Be(false); + } + + [TestMethod] + public void WithIdentifierHtml() + { + var node = new DotNode() + .WithIdentifier("Test", true); + + node.Identifier.Value.Should().Be("Test"); + node.Identifier.IsHtml.Should().Be(true); + } + + [TestMethod] + public void WithColorString() + { + var node = new DotNode() + .WithColor("red"); + + node.Color.Value.Should().Be("red"); + } + + [TestMethod] + public void WithColor() + { + var node = new DotNode() + .WithColor(Color.Red); + + node.Color.Value.Should().Be("#FF0000"); + } + + [TestMethod] + public void WithFillColorString() + { + var node = new DotNode() + .WithFillColor("red"); + + node.FillColor.Value.Should().Be("red"); + } + + [TestMethod] + public void WithFillColor() + { + var node = new DotNode() + .WithFillColor(Color.Red); + + node.FillColor.Value.Should().Be("#FF0000"); + } + + [TestMethod] + public void WithShapeString() + { + var node = new DotNode() + .WithShape("custom"); + + node.Shape.Value.Should().Be("custom"); + } + + [TestMethod] + public void WithShape() + { + var node = new DotNode() + .WithShape(DotNodeShape.Diamond); + + node.Shape.Value.Should().Be("diamond"); + } + + [TestMethod] + public void WithStyleString() + { + var node = new DotNode() + .WithStyle("custom"); + + node.Style.Value.Should().Be("custom"); + } + + [TestMethod] + public void WithStyle() + { + var node = new DotNode() + .WithStyle(DotNodeStyle.Diagonals); + + node.Style.Value.Should().Be("diagonals"); + } + + [TestMethod] + public void WithWidth() + { + var node = new DotNode() + .WithWidth(123.456); + + node.Width.Value.Should().Be(123.456); + } + + [TestMethod] + public void WithHeight() + { + var node = new DotNode() + .WithHeight(123.456); + + node.Height.Value.Should().Be(123.456); + } + + [TestMethod] + public void WithPenWidth() + { + var node = new DotNode() + .WithPenWidth(123.456); + + node.PenWidth.Value.Should().Be(123.456); + } + + [TestMethod] + public void WithPosString() + { + var node = new DotNode() + .WithPos("42,69"); + + node.Pos.Value.Should().Be("42,69"); + } + + [TestMethod] + public void WithPos2D() + { + var node = new DotNode() + .WithPos(42, 69); + + node.Pos.Value.Should().Be("42,69"); + } + + [TestMethod] + public void WithPos3D() + { + var node = new DotNode() + .WithPos(42, 69, 75); + + node.Pos.Value.Should().Be("42,69,75"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Extensions/DotSubgraphExtensionsTests.cs b/Sources/DotNetGraph.Tests/Extensions/DotSubgraphExtensionsTests.cs new file mode 100644 index 0000000..eed343e --- /dev/null +++ b/Sources/DotNetGraph.Tests/Extensions/DotSubgraphExtensionsTests.cs @@ -0,0 +1,48 @@ +using System.Drawing; +using DotNetGraph.Core; +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Extensions; + +[TestClass] +public class DotSubgraphExtensionsTests +{ + [TestMethod] + public void WithColorString() + { + var subgraph = new DotSubgraph() + .WithColor("red"); + + subgraph.Color.Value.Should().Be("red"); + } + + [TestMethod] + public void WithColor() + { + var subgraph = new DotSubgraph() + .WithColor(Color.Red); + + subgraph.Color.Value.Should().Be("#FF0000"); + } + + + [TestMethod] + public void WithStyleString() + { + var subgraph = new DotSubgraph() + .WithStyle("custom"); + + subgraph.Style.Value.Should().Be("custom"); + } + + [TestMethod] + public void WithStyle() + { + var subgraph = new DotSubgraph() + .WithStyle(DotSubgraphStyle.Striped); + + subgraph.Style.Value.Should().Be("striped"); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Extensions/EnumExtensionsTests.cs b/Sources/DotNetGraph.Tests/Extensions/EnumExtensionsTests.cs index af981a3..fb12cdf 100644 --- a/Sources/DotNetGraph.Tests/Extensions/EnumExtensionsTests.cs +++ b/Sources/DotNetGraph.Tests/Extensions/EnumExtensionsTests.cs @@ -1,42 +1,65 @@ -using DotNetGraph.Extensions; -using NFluent; using System; -using Xunit; +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace DotNetGraph.Tests.Extensions +namespace DotNetGraph.Tests.Extensions; + +[TestClass] +public class EnumExtensionsTests { - public class EnumExtensionsTests + [Flags] + private enum TestFlaggedEnum + { + Hello = 1, + World = 2, + Lorem = 4, + Ipsum = 8 + } + + private enum TestNonFlaggedEnum + { + Hello, + World + } + + [TestMethod] + public void FlagsToStringNoFlag() + { + var value = (TestFlaggedEnum) 0; + + var result = value.FlagsToString(); + + result.Should().BeEmpty(); + } + + [TestMethod] + public void FlagsToStringOneFlag() { - public enum EnumWithoutFlags - { - One, Two, Three - } - - [Flags] - public enum EnumWithFlags - { - One = 1, - Two = 2, - Three = 4 - } - - [Fact] - public void FlagsToString_WhenEnumWithoutFlagsProvided_ThenThereIsAnException() - { - var e = EnumWithoutFlags.One | EnumWithoutFlags.Three; - - Check.ThatCode(() => e.FlagsToString()) - .Throws(); - } - - [Theory] - [InlineData(EnumWithFlags.One, "one")] - [InlineData(default(EnumWithFlags), "")] - [InlineData(EnumWithFlags.One | EnumWithFlags.Three, "one,three")] - public void FlagsToString_WhenEnumWithFlagsProvided_ThenCorrectStringIsReturned(EnumWithFlags e, string expected) - { - var result = e.FlagsToString(); - Check.That(result).HasSameValueAs(expected); - } + var value = TestFlaggedEnum.Lorem; + + var result = value.FlagsToString(); + + result.Should().Be("lorem"); + } + + [TestMethod] + public void FlagsToStringMultipleFlags() + { + var value = TestFlaggedEnum.Hello | TestFlaggedEnum.World | TestFlaggedEnum.Ipsum; + + var result = value.FlagsToString(); + + result.Should().Be("hello,world,ipsum"); + } + + [TestMethod] + public void FlagsToStringNonFlaggedEnum() + { + var value = TestNonFlaggedEnum.Hello; + + value.Invoking(v => v.FlagsToString()) + .Should() + .Throw(); } -} +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Extensions/StringExtensionsTests.cs b/Sources/DotNetGraph.Tests/Extensions/StringExtensionsTests.cs new file mode 100644 index 0000000..5a7f0a6 --- /dev/null +++ b/Sources/DotNetGraph.Tests/Extensions/StringExtensionsTests.cs @@ -0,0 +1,22 @@ +using DotNetGraph.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DotNetGraph.Tests.Extensions; + +[TestClass] +public class StringExtensionsTests +{ + [DataTestMethod] + [DataRow("", "")] + [DataRow("Hello,\r\n world!", "Hello,\\n world!")] + [DataRow("Hello,\n world!", "Hello,\\n world!")] + [DataRow("Hello, \"world\"!", "Hello, \\\"world\\\"!")] + [DataRow("C:\\", "C:\\\\")] + public void FormatGraphvizEscapedCharacters(string input, string expectedOutput) + { + var result = input.FormatGraphvizEscapedCharacters(); + + result.Should().Be(expectedOutput); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Indentation/IndentedGraphTests.cs b/Sources/DotNetGraph.Tests/Indentation/IndentedGraphTests.cs deleted file mode 100644 index d98b5d6..0000000 --- a/Sources/DotNetGraph.Tests/Indentation/IndentedGraphTests.cs +++ /dev/null @@ -1,179 +0,0 @@ -using System.Drawing; -using DotNetGraph.Edge; -using DotNetGraph.Extensions; -using DotNetGraph.Node; -using DotNetGraph.SubGraph; -using NFluent; -using Xunit; -using Xunit.Abstractions; - -namespace DotNetGraph.Tests.Indentation -{ - public class IndentedGraphTests - { - private readonly ITestOutputHelper _output; - - public IndentedGraphTests(ITestOutputHelper output) - { - _output = output; - } - - [Fact] - public void BasicIndentedGraph() - { - var graph = new DotGraph("TestGraph"); - - var compiled = graph.Compile(true); - - _output.WriteLine(compiled); - - Check.That(compiled).HasSameValueAs("graph TestGraph { \n}"); - } - - [Fact] - public void BasicIndentedDirectedGraph() - { - var graph = new DotGraph("TestGraph", true); - - var compiled = graph.Compile(true); - - _output.WriteLine(compiled); - - Check.That(compiled).HasSameValueAs("digraph TestGraph { \n}"); - } - - [Fact] - public void BasicIndentedEdge() - { - var graph = new DotGraph("TestGraph"); - - graph.Elements.Add(new DotEdge("A", "B")); - - var compiled = graph.Compile(true); - - _output.WriteLine(compiled); - - Check.That(compiled).HasSameValueAs("graph TestGraph { \n\tA -- B; \n}"); - } - - [Fact] - public void IndentedEdgeWithAttributes() - { - var graph = new DotGraph("TestGraph"); - - var edge = new DotEdge("A", "B") - { - Style = DotEdgeStyle.Dashed, - Color = Color.Red - }; - - graph.Elements.Add(edge); - - var compiled = graph.Compile(true); - - _output.WriteLine(compiled); - - Check.That(compiled).HasSameValueAs("graph TestGraph { \n\tA -- B[style=dashed,color=\"#FF0000\"]; \n}"); - } - - [Fact] - public void BasicIndentedSubGraph() - { - var graph = new DotGraph("TestGraph"); - - graph.Elements.Add(new DotSubGraph("TestSubGraph")); - - var compiled = graph.Compile(true); - - _output.WriteLine(compiled); - - Check.That(compiled).HasSameValueAs("graph TestGraph { \n\tsubgraph TestSubGraph { \n\t} \n}"); - } - - [Fact] - public void IndentedSubGraphWithEdge() - { - var graph = new DotGraph("TestGraph"); - - var subGraph = new DotSubGraph("TestSubGraph"); - - subGraph.Elements.Add(new DotEdge("A", "B")); - - graph.Elements.Add(subGraph); - - var compiled = graph.Compile(true); - - _output.WriteLine(compiled); - - Check.That(compiled).HasSameValueAs("graph TestGraph { \n\tsubgraph TestSubGraph { \n\t\tA -- B; \n\t} \n}"); - } - - [Fact] - public void BasicIndentedNode() - { - var graph = new DotGraph("TestGraph"); - - graph.Elements.Add(new DotNode("TestNode")); - - var compiled = graph.Compile(true); - - _output.WriteLine(compiled); - - Check.That(compiled).HasSameValueAs("graph TestGraph { \n\tTestNode; \n}"); - } - - [Fact] - public void IndentedNodeWithAttributes() - { - var graph = new DotGraph("TestGraph"); - - var edge = new DotNode("TestNode") - { - Color = Color.Red, - Style = DotNodeStyle.Bold - }; - - graph.Elements.Add(edge); - - var compiled = graph.Compile(true); - - _output.WriteLine(compiled); - - Check.That(compiled).HasSameValueAs("graph TestGraph { \n\tTestNode[color=\"#FF0000\",style=bold]; \n}"); - } - - [Fact] - public void DotGraph_WhenNodesSubGraphsEdgesAndCustomLinesProvided_ThenCompilationIsFormattedCorrectly() - { - var graph = new DotGraph("TestGraph", true) - .AddLine("rankdir = TB;") - .AddSubGraph("cluster_0", s => - { - s.Label = "Test Sub Graph"; - s.AddNode("A") - .AddNode("B") - .AddLine("{rank = same; A; X;}"); - }) - .AddEdge("A", "B"); - - var compiled = graph.Compile(true); - - _output.WriteLine(compiled); - - // digraph TestGraph { - // rankdir = TB; - // subgraph cluster_0 { - // label="Test Sub Graph"; - // A; - // B; - // {rank = same; A; X;} - // } - // A -> B; - // } - - var expected = "digraph TestGraph { \n\trankdir = TB; \n\tsubgraph cluster_0 { \n\t\tlabel=\"Test Sub Graph\"; \n\t\tA; \n\t\tB; \n\t\t{rank = same; A; X;} \n\t} \n\tA -> B; \n}"; - - Check.That(compiled).HasSameValueAs(expected); - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/Node/BasicNodeTests.cs b/Sources/DotNetGraph.Tests/Node/BasicNodeTests.cs deleted file mode 100644 index bb99ad1..0000000 --- a/Sources/DotNetGraph.Tests/Node/BasicNodeTests.cs +++ /dev/null @@ -1,362 +0,0 @@ -using System; -using System.Drawing; -using System.Globalization; -using System.Threading; -using DotNetGraph.Core; -using DotNetGraph.Extensions; -using DotNetGraph.Node; -using NFluent; -using Xunit; - -namespace DotNetGraph.Tests.Node -{ - public class BasicNodeTests - { - [Fact] - public void EmptyNode() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode; }"); - } - - [Fact] - public void NodeWithMultipleAttributes() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - { - Color = Color.Blue, - Label = "Test", - Shape = DotNodeShape.Box - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[color=\"#0000FF\",label=Test,shape=box]; }"); - } - - [Fact] - public void NodeWithColor() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode", Color.Red) - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[color=\"#FF0000\"]; }"); - } - - [Fact] - public void NodeWithPosition() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - { - Position = new DotPosition(4, 2) - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[pos=\"4,2!\"]; }"); - } - - [Fact] - public void NodeWithShape() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - { - Shape = DotNodeShape.Square - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[shape=square]; }"); - } - - [Fact] - public void NodeWithStyle() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - { - Style = DotNodeStyle.Dashed - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[style=dashed]; }"); - } - - [Fact] - public void NodeWithMultipleStyles() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - { - Style = DotNodeStyle.Rounded | DotNodeStyle.Filled - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[style=\"rounded,filled\"]; }"); - } - - [Fact] - public void NodeWithFontColor() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - { - FontColor = Color.Red - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[fontcolor=\"#FF0000\"]; }"); - } - - [Fact] - public void NodeWithFillColor() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - { - FillColor = Color.Red - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[fillcolor=\"#FF0000\"]; }"); - } - - [Fact] - public void NodeWithLabel() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - { - Label = "Hello, \"world\"!" - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[label=\"Hello, \\\"world\\\"!\"]; }"); - } - - [Fact] - public void NodeWithPenWidth() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - { - PenWidth = 0.64f - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[penwidth=0.64]; }"); - } - - [Fact] - public void NodeWithWidth() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - { - Width = 0.64f - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[width=0.64]; }"); - } - - [Fact] - public void NodeWithHeight() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - { - Height = 0.64f - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[height=0.64]; }"); - } - - [Fact] - public void NodeWithWidthAndHeightUsesCorrectCulture() - { - var currentCulture = Thread.CurrentThread.CurrentCulture; - var currentUiCulture = Thread.CurrentThread.CurrentUICulture; - - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - { - Width = 0.46f, - Height = 0.64f - } - } - }; - - var cultureInfo = new CultureInfo("de-DE"); - Thread.CurrentThread.CurrentCulture = cultureInfo; - Thread.CurrentThread.CurrentUICulture = cultureInfo; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[width=0.46,height=0.64]; }"); - - Thread.CurrentThread.CurrentCulture = currentCulture; - Thread.CurrentThread.CurrentUICulture = currentUiCulture; - } - - [Fact] - public void NodeWithLargeHeightUsesCorrectCulture() - { - var currentCulture = Thread.CurrentThread.CurrentCulture; - var currentUiCulture = Thread.CurrentThread.CurrentUICulture; - - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotNode("TestNode") - { - Height = 12345.67f - } - } - }; - - var cultureInfo = new CultureInfo("fr-FR"); - Thread.CurrentThread.CurrentCulture = cultureInfo; - Thread.CurrentThread.CurrentUICulture = cultureInfo; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[height=12345.67]; }"); - - Thread.CurrentThread.CurrentCulture = currentCulture; - Thread.CurrentThread.CurrentUICulture = currentUiCulture; - } - - [Fact] - public void NodeWithNullIdentifierThrowsException() - { - Check.ThatCode(() => new DotNode(null)).Throws(); - } - - [Fact] - public void NodeWithEmptyIdentifierThrowsException() - { - Check.ThatCode(() => new DotNode(string.Empty)).Throws(); - Check.ThatCode(() => new DotNode(" ")).Throws(); - } - - [Fact] - public void ModifyNodeIdentifierWithNullIdentifierThrowsException() - { - var node = new DotNode("test"); - Check.ThatCode(() => node.Identifier = null).Throws(); - } - - [Fact] - public void ModifyNodeIdentifierWithEmptyIdentifierThrowsException() - { - var node = new DotNode("test"); - Check.ThatCode(() => node.Identifier = string.Empty).Throws(); - Check.ThatCode(() => node.Identifier = " ").Throws(); - } - - [Theory] - [InlineData("shape")] - [InlineData("Shape")] - [InlineData("SHAPE")] - public void DotNode_WhenCustomAttributeSet_ThenItsCompiled(string shapeName) - { - var graph = new DotGraph("TestGraph") - .AddNode("TestNode", n => - { - n.SetCustomAttribute(shapeName, "square"); - }); - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { TestNode[shape=square]; }"); - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph.Tests/SubGraph/BasicSubGraphTests.cs b/Sources/DotNetGraph.Tests/SubGraph/BasicSubGraphTests.cs deleted file mode 100644 index 4f088c7..0000000 --- a/Sources/DotNetGraph.Tests/SubGraph/BasicSubGraphTests.cs +++ /dev/null @@ -1,248 +0,0 @@ -using System.Drawing; -using DotNetGraph.Edge; -using DotNetGraph.Extensions; -using DotNetGraph.Node; -using DotNetGraph.SubGraph; -using NFluent; -using Xunit; - -namespace DotNetGraph.Tests.SubGraph -{ - public class BasicSubGraphTests - { - [Fact] - public void EmptySubGraph() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotSubGraph("TestSubGraph") - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { subgraph TestSubGraph { } }"); - } - - [Fact] - public void SubGraphWithSpaceInIdentifier() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotSubGraph("My test subgraph") - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { subgraph \"My test subgraph\" { } }"); - } - - [Fact] - public void SubGraphWithMultipleAttributes() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotSubGraph("TestSubGraph") - { - Style = DotSubGraphStyle.Dashed, - Color = Color.Red, - Label = "Hello, world!" - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { subgraph TestSubGraph { style=dashed; color=\"#FF0000\"; label=\"Hello, world!\"; } }"); - } - - [Fact] - public void SubGraphWithStyle() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotSubGraph("TestSubGraph") - { - Style = DotSubGraphStyle.Dashed - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { subgraph TestSubGraph { style=dashed; } }"); - } - - [Fact] - public void SubGraphWithMultipleStyles() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotSubGraph("TestSubGraph") - { - Style = DotSubGraphStyle.Rounded | DotSubGraphStyle.Filled - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { subgraph TestSubGraph { style=\"rounded,filled\"; } }"); - } - - [Fact] - public void SubGraphWithLabel() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotSubGraph("TestSubGraph") - { - Label = "Hello, \"world\"!" - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { subgraph TestSubGraph { label=\"Hello, \\\"world\\\"!\"; } }"); - } - - [Fact] - public void SubGraphWithColor() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotSubGraph("TestSubGraph") - { - Color = Color.Red - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { subgraph TestSubGraph { color=\"#FF0000\"; } }"); - } - - [Fact] - public void SubGraphWithEdge() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotSubGraph("TestSubGraph") - { - Elements = - { - new DotEdge("hello", "world") - } - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { subgraph TestSubGraph { hello -- world; } }"); - } - - [Fact] - public void SubGraphWithEdgeDirected() - { - var graph = new DotGraph("TestGraph") - { - Directed = true, - Elements = - { - new DotSubGraph("TestSubGraph") - { - Elements = - { - new DotEdge("hello", "world") - } - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("digraph TestGraph { subgraph TestSubGraph { hello -> world; } }"); - } - - [Fact] - public void SubGraphWithNode() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotSubGraph("TestSubGraph") - { - Elements = - { - new DotNode("TestNode") - } - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { subgraph TestSubGraph { TestNode; } }"); - } - - [Fact] - public void DotSubGraph_WhenRawLineAdded_ThenItsCompiled() - { - var graph = new DotGraph("TestGraph") - { - Elements = - { - new DotSubGraph("TestSubGraph") - { - Elements = - { - new DotString("{rank = same; A; X;}") - } - } - } - }; - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { subgraph TestSubGraph { {rank = same; A; X;} } }"); - } - - [Theory] - [InlineData("rank")] - [InlineData("Rank")] - [InlineData("RANK")] - public void DotSubGraph_WhenCustomAttributeSet_ThenItsCompiled(string rankName) - { - var graph = new DotGraph("TestGraph") - .AddSubGraph("TestSubGraph", s => - { - s.SetCustomAttribute(rankName, "same; A; X;"); - }); - - var compiled = graph.Compile(); - - Check.That(compiled).HasSameValueAs("graph TestGraph { subgraph TestSubGraph { rank=same; A; X; } }"); - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotAttribute.cs b/Sources/DotNetGraph/Attributes/DotAttribute.cs new file mode 100644 index 0000000..9c18c90 --- /dev/null +++ b/Sources/DotNetGraph/Attributes/DotAttribute.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using DotNetGraph.Compilation; + +namespace DotNetGraph.Attributes +{ + public class DotAttribute : IDotAttribute + { + public string Value { get; set; } + + public DotAttribute(string value) + { + Value = value; + } + + public async Task CompileAsync(CompilationContext context) + { + await context.WriteAsync(Value); + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotColorAttribute.cs b/Sources/DotNetGraph/Attributes/DotColorAttribute.cs index 870b714..3ca1550 100644 --- a/Sources/DotNetGraph/Attributes/DotColorAttribute.cs +++ b/Sources/DotNetGraph/Attributes/DotColorAttribute.cs @@ -1,20 +1,30 @@ using System.Drawing; -using DotNetGraph.Core; +using System.Threading.Tasks; +using DotNetGraph.Compilation; +using DotNetGraph.Extensions; namespace DotNetGraph.Attributes { public class DotColorAttribute : IDotAttribute { - public Color Color { get; set; } + public string Value { get; set; } - public DotColorAttribute(Color color = default) + public DotColorAttribute(Color color) { - Color = color; + Value = color.ToHexString(); } - public static implicit operator DotColorAttribute(Color? color) + public DotColorAttribute(string value) { - return color.HasValue ? new DotColorAttribute(color.Value) : null; + Value = value; } + + public async Task CompileAsync(CompilationContext context) + { + await context.WriteAsync($"\"{Value}\""); + } + + public static implicit operator DotColorAttribute(Color value) => new DotColorAttribute(value); + public static implicit operator DotColorAttribute(string value) => new DotColorAttribute(value); } } \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotDoubleAttribute.cs b/Sources/DotNetGraph/Attributes/DotDoubleAttribute.cs new file mode 100644 index 0000000..f052fd5 --- /dev/null +++ b/Sources/DotNetGraph/Attributes/DotDoubleAttribute.cs @@ -0,0 +1,26 @@ +using System.Globalization; +using System.Threading.Tasks; +using DotNetGraph.Compilation; + +namespace DotNetGraph.Attributes +{ + public class DotDoubleAttribute : IDotAttribute + { + public double Value { get; set; } + + public string Format { get; set; } + + public DotDoubleAttribute(double value, string format = "F2") + { + Value = value; + Format = format; + } + + public async Task CompileAsync(CompilationContext context) + { + await context.WriteAsync(Value.ToString(Format, NumberFormatInfo.InvariantInfo)); + } + + public static implicit operator DotDoubleAttribute(double value) => new DotDoubleAttribute(value); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotEdgeArrowHeadAttribute.cs b/Sources/DotNetGraph/Attributes/DotEdgeArrowHeadAttribute.cs deleted file mode 100644 index 43411a1..0000000 --- a/Sources/DotNetGraph/Attributes/DotEdgeArrowHeadAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -using DotNetGraph.Core; -using DotNetGraph.Edge; - -namespace DotNetGraph.Attributes -{ - public class DotEdgeArrowHeadAttribute : IDotAttribute - { - public DotEdgeArrowType ArrowType { get; set; } - - public DotEdgeArrowHeadAttribute(DotEdgeArrowType arrowType = default) - { - ArrowType = arrowType; - } - - public static implicit operator DotEdgeArrowHeadAttribute(DotEdgeArrowType? arrowType) - { - return arrowType.HasValue ? new DotEdgeArrowHeadAttribute(arrowType.Value) : null; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotEdgeArrowTailAttribute.cs b/Sources/DotNetGraph/Attributes/DotEdgeArrowTailAttribute.cs deleted file mode 100644 index b1a06c0..0000000 --- a/Sources/DotNetGraph/Attributes/DotEdgeArrowTailAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -using DotNetGraph.Core; -using DotNetGraph.Edge; - -namespace DotNetGraph.Attributes -{ - public class DotEdgeArrowTailAttribute : IDotAttribute - { - public DotEdgeArrowType ArrowType { get; set; } - - public DotEdgeArrowTailAttribute(DotEdgeArrowType arrowType = default) - { - ArrowType = arrowType; - } - - public static implicit operator DotEdgeArrowTailAttribute(DotEdgeArrowType? arrowType) - { - return arrowType.HasValue ? new DotEdgeArrowTailAttribute(arrowType.Value) : null; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotEdgeArrowTypeAttribute.cs b/Sources/DotNetGraph/Attributes/DotEdgeArrowTypeAttribute.cs new file mode 100644 index 0000000..03d5000 --- /dev/null +++ b/Sources/DotNetGraph/Attributes/DotEdgeArrowTypeAttribute.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using DotNetGraph.Compilation; +using DotNetGraph.Core; + +namespace DotNetGraph.Attributes +{ + public class DotEdgeArrowTypeAttribute : IDotAttribute + { + public string Value { get; set; } + + public DotEdgeArrowTypeAttribute(string value) + { + Value = value; + } + + public DotEdgeArrowTypeAttribute(DotEdgeArrowType type) + { + Value = type.ToString().ToLowerInvariant(); + } + + public async Task CompileAsync(CompilationContext context) + { + await context.WriteAsync($"\"{Value}\""); + } + + public static implicit operator DotEdgeArrowTypeAttribute(DotEdgeArrowType value) => new DotEdgeArrowTypeAttribute(value); + public static implicit operator DotEdgeArrowTypeAttribute(string value) => new DotEdgeArrowTypeAttribute(value); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotEdgeStyleAttribute.cs b/Sources/DotNetGraph/Attributes/DotEdgeStyleAttribute.cs index 7f8c078..59e0f79 100644 --- a/Sources/DotNetGraph/Attributes/DotEdgeStyleAttribute.cs +++ b/Sources/DotNetGraph/Attributes/DotEdgeStyleAttribute.cs @@ -1,19 +1,30 @@ -using DotNetGraph.Edge; +using System.Threading.Tasks; +using DotNetGraph.Compilation; +using DotNetGraph.Core; +using DotNetGraph.Extensions; namespace DotNetGraph.Attributes { - public class DotEdgeStyleAttribute : DotColorAttribute + public class DotEdgeStyleAttribute : IDotAttribute { - public DotEdgeStyle Style { get; set; } + public string Value { get; set; } - public DotEdgeStyleAttribute(DotEdgeStyle style = default) + public DotEdgeStyleAttribute(string value) { - Style = style; + Value = value; } - public static implicit operator DotEdgeStyleAttribute(DotEdgeStyle? style) + public DotEdgeStyleAttribute(DotEdgeStyle style) { - return style.HasValue ? new DotEdgeStyleAttribute(style.Value) : null; + Value = style.FlagsToString(); } + + public async Task CompileAsync(CompilationContext context) + { + await context.WriteAsync($"\"{Value}\""); + } + + public static implicit operator DotEdgeStyleAttribute(DotEdgeStyle value) => new DotEdgeStyleAttribute(value); + public static implicit operator DotEdgeStyleAttribute(string value) => new DotEdgeStyleAttribute(value); } -} +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotFillColorAttribute.cs b/Sources/DotNetGraph/Attributes/DotFillColorAttribute.cs deleted file mode 100644 index b2bc948..0000000 --- a/Sources/DotNetGraph/Attributes/DotFillColorAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Drawing; - -namespace DotNetGraph.Attributes -{ - public class DotFillColorAttribute : DotColorAttribute - { - public DotFillColorAttribute(Color color = default) : base(color) - { - } - - public static implicit operator DotFillColorAttribute(Color? color) - { - return color.HasValue ? new DotFillColorAttribute(color.Value) : null; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotFontColorAttribute.cs b/Sources/DotNetGraph/Attributes/DotFontColorAttribute.cs deleted file mode 100644 index daf93a1..0000000 --- a/Sources/DotNetGraph/Attributes/DotFontColorAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Drawing; - -namespace DotNetGraph.Attributes -{ - public class DotFontColorAttribute : DotColorAttribute - { - public DotFontColorAttribute(Color color = default) : base(color) - { - } - - public static implicit operator DotFontColorAttribute(Color? color) - { - return color.HasValue ? new DotFontColorAttribute(color.Value) : null; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotLabelAttribute.cs b/Sources/DotNetGraph/Attributes/DotLabelAttribute.cs index 5c6aff7..bf0b50b 100644 --- a/Sources/DotNetGraph/Attributes/DotLabelAttribute.cs +++ b/Sources/DotNetGraph/Attributes/DotLabelAttribute.cs @@ -1,19 +1,33 @@ -using DotNetGraph.Core; +using System.Threading.Tasks; +using DotNetGraph.Compilation; +using DotNetGraph.Extensions; namespace DotNetGraph.Attributes { public class DotLabelAttribute : IDotAttribute { - public string Text { get; set; } - - public DotLabelAttribute(string text = default) + public string Value { get; set; } + + public bool IsHtml { get; set; } + + public DotLabelAttribute(string value, bool isHtml = false) { - Text = text; + Value = value; + IsHtml = isHtml; } - public static implicit operator DotLabelAttribute(string text) + public async Task CompileAsync(CompilationContext context) { - return text is null ? null : new DotLabelAttribute(text); + if (IsHtml) + { + await context.TextWriter.WriteAsync($"<{Value}>"); + return; + } + + var value = context.Options.AutomaticEscapedCharactersFormat ? Value.FormatGraphvizEscapedCharacters() : Value; + await context.TextWriter.WriteAsync($"\"{value}\""); } + + public static implicit operator DotLabelAttribute(string value) => new DotLabelAttribute(value); } } \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotNodeHeightAttribute.cs b/Sources/DotNetGraph/Attributes/DotNodeHeightAttribute.cs deleted file mode 100644 index 6d1aa2a..0000000 --- a/Sources/DotNetGraph/Attributes/DotNodeHeightAttribute.cs +++ /dev/null @@ -1,24 +0,0 @@ -using DotNetGraph.Core; - -namespace DotNetGraph.Attributes -{ - public class DotNodeHeightAttribute : IDotAttribute - { - public float Value { get; set; } - - public DotNodeHeightAttribute(float value = default) - { - Value = value; - } - - public static implicit operator DotNodeHeightAttribute(float? value) - { - return value.HasValue ? new DotNodeHeightAttribute(value.Value) : null; - } - - public static implicit operator DotNodeHeightAttribute(int? value) - { - return value.HasValue ? new DotNodeHeightAttribute(value.Value) : null; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotNodeShapeAttribute.cs b/Sources/DotNetGraph/Attributes/DotNodeShapeAttribute.cs index e32be94..b8c81d7 100644 --- a/Sources/DotNetGraph/Attributes/DotNodeShapeAttribute.cs +++ b/Sources/DotNetGraph/Attributes/DotNodeShapeAttribute.cs @@ -1,20 +1,29 @@ +using System.Threading.Tasks; +using DotNetGraph.Compilation; using DotNetGraph.Core; -using DotNetGraph.Node; namespace DotNetGraph.Attributes { public class DotNodeShapeAttribute : IDotAttribute { - public DotNodeShape Shape { get; set; } + public string Value { get; set; } - public DotNodeShapeAttribute(DotNodeShape shape = default) + public DotNodeShapeAttribute(string value) { - Shape = shape; + Value = value; } - public static implicit operator DotNodeShapeAttribute(DotNodeShape? shape) + public DotNodeShapeAttribute(DotNodeShape shape) { - return shape.HasValue ? new DotNodeShapeAttribute(shape.Value) : null; + Value = shape.ToString().ToLowerInvariant(); } + + public async Task CompileAsync(CompilationContext context) + { + await context.WriteAsync($"\"{Value}\""); + } + + public static implicit operator DotNodeShapeAttribute(DotNodeShape value) => new DotNodeShapeAttribute(value); + public static implicit operator DotNodeShapeAttribute(string value) => new DotNodeShapeAttribute(value); } } \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotNodeStyleAttribute.cs b/Sources/DotNetGraph/Attributes/DotNodeStyleAttribute.cs index 02e208b..be0f557 100644 --- a/Sources/DotNetGraph/Attributes/DotNodeStyleAttribute.cs +++ b/Sources/DotNetGraph/Attributes/DotNodeStyleAttribute.cs @@ -1,20 +1,30 @@ +using System.Threading.Tasks; +using DotNetGraph.Compilation; using DotNetGraph.Core; -using DotNetGraph.Node; +using DotNetGraph.Extensions; namespace DotNetGraph.Attributes { - public class DotNodeStyleAttribute : DotColorAttribute + public class DotNodeStyleAttribute : IDotAttribute { - public DotNodeStyle Style { get; set; } + public string Value { get; set; } - public DotNodeStyleAttribute(DotNodeStyle style = default) + public DotNodeStyleAttribute(string value) { - Style = style; + Value = value; } - public static implicit operator DotNodeStyleAttribute(DotNodeStyle? style) + public DotNodeStyleAttribute(DotNodeStyle style) { - return style.HasValue ? new DotNodeStyleAttribute(style.Value) : null; + Value = style.FlagsToString(); } + + public async Task CompileAsync(CompilationContext context) + { + await context.WriteAsync($"\"{Value}\""); + } + + public static implicit operator DotNodeStyleAttribute(DotNodeStyle value) => new DotNodeStyleAttribute(value); + public static implicit operator DotNodeStyleAttribute(string value) => new DotNodeStyleAttribute(value); } } \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotNodeWidthAttribute.cs b/Sources/DotNetGraph/Attributes/DotNodeWidthAttribute.cs deleted file mode 100644 index 27927c7..0000000 --- a/Sources/DotNetGraph/Attributes/DotNodeWidthAttribute.cs +++ /dev/null @@ -1,24 +0,0 @@ -using DotNetGraph.Core; - -namespace DotNetGraph.Attributes -{ - public class DotNodeWidthAttribute : IDotAttribute - { - public float Value { get; set; } - - public DotNodeWidthAttribute(float value = default) - { - Value = value; - } - - public static implicit operator DotNodeWidthAttribute(float? value) - { - return value.HasValue ? new DotNodeWidthAttribute(value.Value) : null; - } - - public static implicit operator DotNodeWidthAttribute(int? value) - { - return value.HasValue ? new DotNodeWidthAttribute(value.Value) : null; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotPenWidthAttribute.cs b/Sources/DotNetGraph/Attributes/DotPenWidthAttribute.cs deleted file mode 100644 index 9133b8b..0000000 --- a/Sources/DotNetGraph/Attributes/DotPenWidthAttribute.cs +++ /dev/null @@ -1,24 +0,0 @@ -using DotNetGraph.Core; - -namespace DotNetGraph.Attributes -{ - public class DotPenWidthAttribute : IDotAttribute - { - public float Value { get; set; } - - public DotPenWidthAttribute(float value = default) - { - Value = value; - } - - public static implicit operator DotPenWidthAttribute(float? value) - { - return value.HasValue ? new DotPenWidthAttribute(value.Value) : null; - } - - public static implicit operator DotPenWidthAttribute(int? value) - { - return value.HasValue ? new DotPenWidthAttribute(value.Value) : null; - } - } -} diff --git a/Sources/DotNetGraph/Attributes/DotPointAttribute.cs b/Sources/DotNetGraph/Attributes/DotPointAttribute.cs new file mode 100644 index 0000000..5505a01 --- /dev/null +++ b/Sources/DotNetGraph/Attributes/DotPointAttribute.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using DotNetGraph.Compilation; +using DotNetGraph.Core; + +namespace DotNetGraph.Attributes +{ + public class DotPointAttribute : IDotAttribute + { + public string Value { get; set; } + + public DotPointAttribute(string value) + { + Value = value; + } + + public DotPointAttribute(DotPoint point) + { + Value = point.ToString(); + } + + public async Task CompileAsync(CompilationContext context) + { + await context.WriteAsync(Value); + } + + public static implicit operator DotPointAttribute(DotPoint value) => new DotPointAttribute(value); + public static implicit operator DotPointAttribute(string value) => new DotPointAttribute(value); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotPositionAttribute.cs b/Sources/DotNetGraph/Attributes/DotPositionAttribute.cs deleted file mode 100644 index d9aefcd..0000000 --- a/Sources/DotNetGraph/Attributes/DotPositionAttribute.cs +++ /dev/null @@ -1,19 +0,0 @@ -using DotNetGraph.Core; - -namespace DotNetGraph.Attributes -{ - public class DotPositionAttribute : IDotAttribute - { - public DotPosition Position { get; set; } - - public DotPositionAttribute(DotPosition position = default) - { - Position = position; - } - - public static implicit operator DotPositionAttribute(DotPosition position) - { - return new DotPositionAttribute(position); - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotSubGraphStyleAttribute.cs b/Sources/DotNetGraph/Attributes/DotSubGraphStyleAttribute.cs deleted file mode 100644 index fe13031..0000000 --- a/Sources/DotNetGraph/Attributes/DotSubGraphStyleAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -using DotNetGraph.Core; -using DotNetGraph.SubGraph; - -namespace DotNetGraph.Attributes -{ - public class DotSubGraphStyleAttribute : IDotAttribute - { - public DotSubGraphStyle Style { get; set; } - - public DotSubGraphStyleAttribute(DotSubGraphStyle style = default) - { - Style = style; - } - - public static implicit operator DotSubGraphStyleAttribute(DotSubGraphStyle? style) - { - return style.HasValue ? new DotSubGraphStyleAttribute(style.Value) : null; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/DotSubgraphStyleAttribute.cs b/Sources/DotNetGraph/Attributes/DotSubgraphStyleAttribute.cs new file mode 100644 index 0000000..acb22b4 --- /dev/null +++ b/Sources/DotNetGraph/Attributes/DotSubgraphStyleAttribute.cs @@ -0,0 +1,30 @@ +using System.Threading.Tasks; +using DotNetGraph.Compilation; +using DotNetGraph.Core; +using DotNetGraph.Extensions; + +namespace DotNetGraph.Attributes +{ + public class DotSubgraphStyleAttribute : IDotAttribute + { + public string Value { get; set; } + + public DotSubgraphStyleAttribute(string value) + { + Value = value; + } + + public DotSubgraphStyleAttribute(DotSubgraphStyle style) + { + Value = style.FlagsToString(); + } + + public async Task CompileAsync(CompilationContext context) + { + await context.WriteAsync($"\"{Value}\""); + } + + public static implicit operator DotSubgraphStyleAttribute(DotSubgraphStyle value) => new DotSubgraphStyleAttribute(value); + public static implicit operator DotSubgraphStyleAttribute(string value) => new DotSubgraphStyleAttribute(value); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Attributes/IDotAttribute.cs b/Sources/DotNetGraph/Attributes/IDotAttribute.cs new file mode 100644 index 0000000..832a699 --- /dev/null +++ b/Sources/DotNetGraph/Attributes/IDotAttribute.cs @@ -0,0 +1,8 @@ +using DotNetGraph.Core; + +namespace DotNetGraph.Attributes +{ + public interface IDotAttribute : IDotElement + { + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Compilation/CompilationContext.cs b/Sources/DotNetGraph/Compilation/CompilationContext.cs new file mode 100644 index 0000000..88783a1 --- /dev/null +++ b/Sources/DotNetGraph/Compilation/CompilationContext.cs @@ -0,0 +1,44 @@ +using System.IO; +using System.Threading.Tasks; + +namespace DotNetGraph.Compilation +{ + public class CompilationContext + { + public TextWriter TextWriter { get; } + + public CompilationOptions Options { get; } + + public int IndentationLevel { get; set; } + + public bool DirectedGraph { get; set; } + + public CompilationContext(TextWriter textWriter, CompilationOptions options) + { + TextWriter = textWriter; + Options = options; + IndentationLevel = 0; + DirectedGraph = false; + } + + public async Task WriteIndentationAsync() + { + if (!Options.Indented) + return; + + for (var i = 0; i < IndentationLevel; i++) + await TextWriter.WriteAsync("\t"); + } + + public async Task WriteAsync(string value) + { + await TextWriter.WriteAsync(value); + } + + public async Task WriteLineAsync(string value = null) + { + await TextWriter.WriteAsync(value); + await TextWriter.WriteAsync(Options.Indented ? '\n' : ' '); + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Compilation/CompilationOptions.cs b/Sources/DotNetGraph/Compilation/CompilationOptions.cs new file mode 100644 index 0000000..070d8af --- /dev/null +++ b/Sources/DotNetGraph/Compilation/CompilationOptions.cs @@ -0,0 +1,9 @@ +namespace DotNetGraph.Compilation +{ + public class CompilationOptions + { + public bool AutomaticEscapedCharactersFormat { get; set; } = true; + + public bool Indented { get; set; } = true; + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Compiler/DotCompiler.cs b/Sources/DotNetGraph/Compiler/DotCompiler.cs deleted file mode 100644 index 57d8b20..0000000 --- a/Sources/DotNetGraph/Compiler/DotCompiler.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.IO; -using System.Text; - -namespace DotNetGraph.Compiler -{ - public class DotCompiler - { - private readonly DotGraph _graph; - - public DotCompiler(DotGraph graph) - { - _graph = graph ?? throw new ArgumentNullException(nameof(graph)); - } - - public string Compile(bool indented = false, bool formatStrings = true) - { - var builder = new StringBuilder(); - using (var writer = new StringWriter(builder)) - { - Compile(writer, indented, formatStrings); - return builder.ToString(); - } - } - - public void Compile(Stream stream, bool indented = false, bool formatStrings = true) - { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - var writer = new StreamWriter(stream); - Compile(writer, indented, formatStrings); - } - - public void Compile(TextWriter writer, bool indented = false, bool formatStrings = true) - { - if (writer == null) - { - throw new ArgumentNullException(nameof(writer)); - } - - using (var worker = new DotCompilerWorker(_graph, writer, indented, formatStrings)) - { - worker.Compile(); - } - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Compiler/DotCompilerWorker.cs b/Sources/DotNetGraph/Compiler/DotCompilerWorker.cs deleted file mode 100644 index dfcd968..0000000 --- a/Sources/DotNetGraph/Compiler/DotCompilerWorker.cs +++ /dev/null @@ -1,331 +0,0 @@ -using DotNetGraph.Attributes; -using DotNetGraph.Core; -using DotNetGraph.Edge; -using DotNetGraph.Extensions; -using DotNetGraph.Node; -using DotNetGraph.SubGraph; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Text.RegularExpressions; - -namespace DotNetGraph.Compiler -{ - internal sealed class DotCompilerWorker : IDisposable - { - private static readonly Regex ValidIdentifierPattern = new Regex("^([a-zA-Z\\200-\\377_][a-zA-Z\\200-\\3770-9_]*|[-]?(.[0-9]+|[0-9]+(.[0-9]+)?))$"); - - private readonly DotGraph _graph; - private readonly TextWriter _writer; - private readonly bool _disposeWriter; - - public bool Indented { get; } - public bool FormatStrings { get; } - - public DotCompilerWorker(DotGraph graph, - TextWriter writer, - bool indented, - bool formatStrings, - bool disposeWriter = false) - { - _graph = graph ?? throw new ArgumentNullException(nameof(graph)); - _writer = writer ?? throw new ArgumentNullException(nameof(writer)); - Indented = indented; - FormatStrings = formatStrings; - _disposeWriter = disposeWriter; - } - - public void Dispose() - { - if (_disposeWriter) - { - _writer?.Dispose(); - } - } - - public void Compile() - { - CompileGraph(); - _writer.Flush(); - } - - private void CompileGraph() - { - var indentationLevel = 0; - - if (_graph.Strict) - { - _writer.Write("strict "); - } - - _writer.Write(_graph.Directed ? "digraph " : "graph "); - - _writer.Write($"{SurroundStringWithQuotes(_graph.Identifier, FormatStrings)} {{ "); - - _writer.AddIndentationNewLine(Indented); - - indentationLevel++; - - foreach (var element in _graph.Elements) - { - if (element is DotEdge edge) - { - CompileEdge(edge, indentationLevel); - } - else if (element is DotNode node) - { - CompileNode(node, indentationLevel); - } - else if (element is DotSubGraph subGraph) - { - CompileSubGraph(subGraph, indentationLevel); - } - else if (element is DotString stringElement) - { - CompileStringElement(stringElement, indentationLevel); - } - else - { - throw new DotException($"Graph body can't contain element of type: {element.GetType()}"); - } - } - - indentationLevel--; - - _writer.Write("}"); - } - - private void CompileSubGraph(DotSubGraph subGraph, int indentationLevel) - { - _writer.AddIndentation(Indented, indentationLevel); - - _writer.Write($"subgraph {SurroundStringWithQuotes(subGraph.Identifier, FormatStrings)} {{ "); - - _writer.AddIndentationNewLine(Indented); - - indentationLevel++; - - CompileSubGraphAttributes(subGraph.Attributes, indentationLevel); - - foreach (var element in subGraph.Elements) - { - if (element is DotEdge edge) - { - CompileEdge(edge, indentationLevel); - } - else if (element is DotNode node) - { - CompileNode(node, indentationLevel); - } - else if (element is DotSubGraph subSubGraph) - { - CompileSubGraph(subSubGraph, indentationLevel); - } - else if (element is DotString stringElement) - { - CompileStringElement(stringElement, indentationLevel); - } - else - { - throw new DotException($"Subgraph body can't contain element of type: {element.GetType()}"); - } - } - - indentationLevel--; - - _writer.AddIndentation(Indented, indentationLevel); - - _writer.Write("} "); - - _writer.AddIndentationNewLine(Indented); - } - - private void CompileSubGraphAttributes(IReadOnlyList attributes, int indentationLevel) - { - if (attributes.Count == 0) - return; - - foreach (var attribute in attributes) - { - string line; - if (attribute is DotSubGraphStyleAttribute subGraphStyleAttribute) - { - line = $"style={SurroundStringWithQuotes(subGraphStyleAttribute.Style.FlagsToString(), FormatStrings)};"; - } - else if (attribute is DotColorAttribute colorAttribute) - { - line = $"color=\"{colorAttribute.ToHex()}\";"; - } - else if (attribute is DotLabelAttribute labelAttribute) - { - line = $"label={SurroundStringWithQuotes(labelAttribute.Text, FormatStrings)};"; - } - else if (attribute is DotCustomAttribute customAttribute) - { - line = customAttribute.ToString(); - } - else - { - throw new DotException($"Attribute type not supported: {attribute.GetType()}"); - } - - CompileLine(line, indentationLevel); - } - } - - private void CompileEdge(DotEdge edge, int indentationLevel) - { - _writer.AddIndentation(Indented, indentationLevel); - - CompileEdgeEndPoint(edge.Left); - - _writer.Write(_graph.Directed ? " -> " : " -- "); - - CompileEdgeEndPoint(edge.Right); - - CompileAttributes(edge.Attributes); - - _writer.Write("; "); - - _writer.AddIndentationNewLine(Indented); - } - - private void CompileEdgeEndPoint(IDotElement endPoint) - { - if (endPoint is DotString leftEdgeString) - { - _writer.Write(SurroundStringWithQuotes(leftEdgeString.Value, FormatStrings)); - } - else if (endPoint is DotNode leftEdgeNode) - { - _writer.Write(SurroundStringWithQuotes(leftEdgeNode.Identifier, FormatStrings)); - } - else - { - throw new DotException($"Endpoint of an edge can't be of type: {endPoint.GetType()}"); - } - } - - private void CompileNode(DotNode node, int indentationLevel) - { - _writer.AddIndentation(Indented, indentationLevel); - - _writer.Write(SurroundStringWithQuotes(node.Identifier, FormatStrings)); - - CompileAttributes(node.Attributes); - - _writer.Write("; "); - - _writer.AddIndentationNewLine(Indented); - } - - private void CompileAttributes(IReadOnlyList attributes) - { - if (attributes.Count == 0) - return; - - _writer.Write("["); - - var attributeValues = new List(); - - foreach (var attribute in attributes) - { - if (attribute is DotNodeShapeAttribute nodeShapeAttribute) - { - attributeValues.Add($"shape={nodeShapeAttribute.Shape.ToString().ToLowerInvariant()}"); - } - else if (attribute is DotNodeStyleAttribute nodeStyleAttribute) - { - attributeValues.Add($"style={SurroundStringWithQuotes(nodeStyleAttribute.Style.FlagsToString(), FormatStrings)}"); - } - else if (attribute is DotEdgeStyleAttribute edgeStyleAttribute) - { - attributeValues.Add($"style={SurroundStringWithQuotes(edgeStyleAttribute.Style.FlagsToString(), FormatStrings)}"); - } - else if (attribute is DotFontColorAttribute fontColorAttribute) - { - attributeValues.Add($"fontcolor=\"{fontColorAttribute.ToHex()}\""); - } - else if (attribute is DotFillColorAttribute fillColorAttribute) - { - attributeValues.Add($"fillcolor=\"{fillColorAttribute.ToHex()}\""); - } - else if (attribute is DotColorAttribute colorAttribute) - { - attributeValues.Add($"color=\"{colorAttribute.ToHex()}\""); - } - else if (attribute is DotLabelAttribute labelAttribute) - { - attributeValues.Add($"label={SurroundStringWithQuotes(labelAttribute.Text, FormatStrings)}"); - } - else if (attribute is DotNodeWidthAttribute nodeWidthAttribute) - { - attributeValues.Add(string.Format(CultureInfo.InvariantCulture, "width={0:F2}", nodeWidthAttribute.Value)); - } - else if (attribute is DotNodeHeightAttribute nodeHeightAttribute) - { - attributeValues.Add(string.Format(CultureInfo.InvariantCulture, "height={0:F2}", nodeHeightAttribute.Value)); - } - else if (attribute is DotPenWidthAttribute dotPenwidthAttribute) - { - attributeValues.Add(string.Format(CultureInfo.InvariantCulture, "penwidth={0:F2}", dotPenwidthAttribute.Value)); - } - else if (attribute is DotEdgeArrowTailAttribute edgeArrowTailAttribute) - { - attributeValues.Add($"arrowtail={edgeArrowTailAttribute.ArrowType.ToString().ToLowerInvariant()}"); - } - else if (attribute is DotEdgeArrowHeadAttribute edgeArrowHeadAttribute) - { - attributeValues.Add($"arrowhead={edgeArrowHeadAttribute.ArrowType.ToString().ToLowerInvariant()}"); - } - else if (attribute is DotPositionAttribute positionAttribute && positionAttribute.Position != null) - { - attributeValues.Add($"pos=\"{positionAttribute.Position.X},{positionAttribute.Position.Y}!\""); - } - else if (attribute is DotCustomAttribute customAttribute) - { - attributeValues.Add(customAttribute.ToString()); - } - else - { - throw new DotException($"Attribute type not supported: {attribute.GetType()}"); - } - } - - _writer.Write(string.Join(",", attributeValues)); - - _writer.Write("]"); - } - - private void CompileStringElement(DotString stringElement, int indentationLevel) - { - CompileLine(stringElement.Value, indentationLevel); - } - - private void CompileLine(string value, int indentationLevel) - { - _writer.AddIndentation(Indented, indentationLevel); - _writer.Write(value + " "); - _writer.AddIndentationNewLine(Indented); - } - - internal static string SurroundStringWithQuotes(string value, bool format) - { - var formatted = FormatString(value, format); - return ValidIdentifierPattern.IsMatch(value) ? formatted : "\"" + formatted + "\""; - } - - internal static string FormatString(string value, bool format) - { - if (!format) - return value; - - return value - .Replace("\\", "\\\\") - .Replace("\"", "\\\"") - .Replace("\r\n", "\\n") - .Replace("\n", "\\n"); - } - } -} diff --git a/Sources/DotNetGraph/Core/DotBaseGraph.cs b/Sources/DotNetGraph/Core/DotBaseGraph.cs new file mode 100644 index 0000000..623e578 --- /dev/null +++ b/Sources/DotNetGraph/Core/DotBaseGraph.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using DotNetGraph.Compilation; + +namespace DotNetGraph.Core +{ + public abstract class DotBaseGraph : DotElement + { + public DotIdentifier Identifier { get; set; } + + public DotRankDir? RankDir { get; set; } + + public List Elements { get; } = new List(); + + public abstract override Task CompileAsync(CompilationContext context); + + protected async Task CompileBodyAsync(CompilationContext context) + { + await Identifier.CompileAsync(context); + await context.WriteLineAsync(" {"); + context.IndentationLevel++; + if (RankDir != null) + { + await context.WriteIndentationAsync(); + await context.WriteLineAsync($"rankdir=\"{RankDir.ToString()}\""); + } + await CompileAttributesAsync(context); + context.IndentationLevel--; + await context.WriteIndentationAsync(); + await context.WriteLineAsync("}"); + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Core/DotCustomAttribute.cs b/Sources/DotNetGraph/Core/DotCustomAttribute.cs deleted file mode 100644 index b84b333..0000000 --- a/Sources/DotNetGraph/Core/DotCustomAttribute.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace DotNetGraph.Core -{ - public class DotCustomAttribute : IDotAttribute - { - public DotCustomAttribute(string name, string value) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentException("Argument name cannot be empty", nameof(name)); - } - - Name = name; - Value = value; - } - - public string Name { get; } - public string Value { get; } - - public override string ToString() - { - return $"{Name.ToLowerInvariant()}={Value}"; - } - } -} diff --git a/Sources/DotNetGraph/Core/DotEdge.cs b/Sources/DotNetGraph/Core/DotEdge.cs new file mode 100644 index 0000000..7f13640 --- /dev/null +++ b/Sources/DotNetGraph/Core/DotEdge.cs @@ -0,0 +1,75 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; + +namespace DotNetGraph.Core +{ + public class DotEdge : DotElement + { + public DotIdentifier From { get; set; } + + public DotIdentifier To { get; set; } + + public DotColorAttribute Color + { + get => GetAttributeOrDefault("color"); + set => SetAttribute("color", value); + } + + public DotEdgeStyleAttribute Style + { + get => GetAttributeOrDefault("style"); + set => SetAttribute("style", value); + } + + public DotDoubleAttribute PenWidth + { + get => GetAttribute("penwidth"); + set => SetAttribute("penwidth", value); + } + + public DotEdgeArrowTypeAttribute ArrowHead + { + get => GetAttribute("arrowhead"); + set => SetAttribute("arrowhead", value); + } + + public DotEdgeArrowTypeAttribute ArrowTail + { + get => GetAttribute("arrowtail"); + set => SetAttribute("arrowtail", value); + } + + public DotPointAttribute Pos + { + get => GetAttribute("pos"); + set => SetAttribute("pos", value); + } + + public override async Task CompileAsync(CompilationContext context) + { + if (From is null || To is null) + throw new Exception("Can't compile edge with null From and/or To"); + + await context.WriteIndentationAsync(); + await From.CompileAsync(context); + await context.WriteAsync($" {(context.DirectedGraph ? "->" : "--")} "); + await To.CompileAsync(context); + if (Attributes.Any()) + { + await context.WriteLineAsync(" ["); + context.IndentationLevel++; + await CompileAttributesAsync(context); + context.IndentationLevel--; + await context.WriteIndentationAsync(); + await context.WriteLineAsync("]"); + } + else + { + await context.WriteLineAsync(); + } + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Edge/DotEdgeArrowType.cs b/Sources/DotNetGraph/Core/DotEdgeArrowType.cs similarity index 92% rename from Sources/DotNetGraph/Edge/DotEdgeArrowType.cs rename to Sources/DotNetGraph/Core/DotEdgeArrowType.cs index 6818dbd..a72635c 100644 --- a/Sources/DotNetGraph/Edge/DotEdgeArrowType.cs +++ b/Sources/DotNetGraph/Core/DotEdgeArrowType.cs @@ -1,4 +1,4 @@ -namespace DotNetGraph.Edge +namespace DotNetGraph.Core { public enum DotEdgeArrowType { diff --git a/Sources/DotNetGraph/Edge/DotEdgeStyle.cs b/Sources/DotNetGraph/Core/DotEdgeStyle.cs similarity index 60% rename from Sources/DotNetGraph/Edge/DotEdgeStyle.cs rename to Sources/DotNetGraph/Core/DotEdgeStyle.cs index cddeb27..f1795cc 100644 --- a/Sources/DotNetGraph/Edge/DotEdgeStyle.cs +++ b/Sources/DotNetGraph/Core/DotEdgeStyle.cs @@ -1,6 +1,6 @@ using System; -namespace DotNetGraph.Edge +namespace DotNetGraph.Core { [Flags] public enum DotEdgeStyle @@ -8,6 +8,8 @@ public enum DotEdgeStyle Solid = 1, Dashed = 2, Dotted = 4, - Bold = 8 + Bold = 8, + Tapered = 16, + Invis = 32 } } \ No newline at end of file diff --git a/Sources/DotNetGraph/Core/DotElement.cs b/Sources/DotNetGraph/Core/DotElement.cs new file mode 100644 index 0000000..1bd2cde --- /dev/null +++ b/Sources/DotNetGraph/Core/DotElement.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; + +namespace DotNetGraph.Core +{ + public abstract class DotElement : IDotElement + { + protected readonly Dictionary Attributes = new Dictionary(); + + // Common attributes + public DotLabelAttribute Label + { + get => GetAttributeOrDefault("label"); + set => SetAttribute("label", value); + } + + public DotColorAttribute FontColor + { + get => GetAttributeOrDefault("fontcolor"); + set => SetAttribute("fontcolor", value); + } + + // Attribute methods + public bool HasAttribute(string name) + { + return Attributes.ContainsKey(name); + } + + public IDotAttribute GetAttribute(string name) + { + if (Attributes.TryGetValue(name, out var attribute)) + return attribute; + throw new Exception($"There is no attribute named '{name}'"); + } + + public IDotAttribute GetAttributeOrDefault(string name, IDotAttribute defaultValue = default) + { + if (Attributes.TryGetValue(name, out var attribute)) + return attribute; + return defaultValue; + } + + public T GetAttribute(string name) where T : IDotAttribute + { + var attribute = GetAttribute(name); + if (attribute is T result) + return result; + throw new Exception($"Attribute with name '{name}' doesn't match the expected type (expected: {typeof(T)}, current: {attribute.GetType()})"); + } + + public T GetAttributeOrDefault(string name, T defaultValue = default) where T : IDotAttribute + { + if (Attributes.TryGetValue(name, out var attribute)) + { + if (attribute is T result) + return result; + throw new Exception($"Attribute with name '{name}' doesn't match the expected type (expected: {typeof(T)}, current: {attribute.GetType()})"); + } + + return defaultValue; + } + + public bool TryGetAttribute(string name, out IDotAttribute attribute) + { + var result = Attributes.TryGetValue(name, out var outAttribute); + if (result) + { + attribute = outAttribute; + return true; + } + + attribute = default; + return false; + } + + public bool TryGetAttribute(string name, out T attribute) where T : IDotAttribute + { + var result = TryGetAttribute(name, out var untypedAttribute); + if (result is false) + { + attribute = default; + return false; + } + + if (untypedAttribute is T typedAttribute) + { + attribute = typedAttribute; + return true; + } + + throw new Exception($"Attribute with name '{name}' doesn't match the expected type (expected: {typeof(T)}, current: {untypedAttribute.GetType()})"); + } + + public void SetAttribute(string name, IDotAttribute value) + { + if (value is null) + RemoveAttribute(name); + else + Attributes[name] = value; + } + + public bool RemoveAttribute(string name) + { + return Attributes.Remove(name); + } + + protected async Task CompileAttributesAsync(CompilationContext context) + { + foreach (var attributePair in Attributes) + { + await context.WriteIndentationAsync(); + await context.WriteAsync($"\"{attributePair.Key}\"="); + await attributePair.Value.CompileAsync(context); + await context.WriteLineAsync(); + } + } + + public abstract Task CompileAsync(CompilationContext context); + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Core/DotElementWithAttributes.cs b/Sources/DotNetGraph/Core/DotElementWithAttributes.cs deleted file mode 100644 index 1fdbdcc..0000000 --- a/Sources/DotNetGraph/Core/DotElementWithAttributes.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using DotNetGraph.Attributes; - -namespace DotNetGraph.Core -{ - public class DotElementWithAttributes : IDotElement - { - public ReadOnlyCollection Attributes => _attributes.Values - .Concat(_customAttributes.Values) - .ToList() - .AsReadOnly(); - - private readonly Dictionary _attributes; - private readonly Dictionary _customAttributes = new Dictionary(StringComparer.OrdinalIgnoreCase); - - public DotElementWithAttributes(string identifier = null, DotColorAttribute color = null) - { - _attributes = new Dictionary(); - } - - protected T GetAttribute() where T : IDotAttribute - { - if (_attributes.TryGetValue(typeof(T).Name, out var colorAttribute)) - return (T)colorAttribute; - return default; - } - - protected void SetAttribute(T value) where T : IDotAttribute - { - if (value != null) - _attributes[typeof(T).Name] = value; - else if (_attributes.ContainsKey(typeof(T).Name)) - _attributes.Remove(typeof(T).Name); - } - - protected void SetCustomAttributeInternal(string name, string value) - { - var attr = new DotCustomAttribute(name, value); - _customAttributes[name] = attr; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Core/DotException.cs b/Sources/DotNetGraph/Core/DotException.cs deleted file mode 100644 index 1a504bb..0000000 --- a/Sources/DotNetGraph/Core/DotException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace DotNetGraph.Core -{ - public class DotException : Exception - { - public DotException(string message) : base(message) - { - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Core/DotGraph.cs b/Sources/DotNetGraph/Core/DotGraph.cs new file mode 100644 index 0000000..f83306e --- /dev/null +++ b/Sources/DotNetGraph/Core/DotGraph.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using DotNetGraph.Compilation; + +namespace DotNetGraph.Core +{ + public class DotGraph : DotBaseGraph + { + public bool Strict { get; set; } + + public bool Directed { get; set; } + + public override async Task CompileAsync(CompilationContext context) + { + context.DirectedGraph = Directed; + await context.WriteIndentationAsync(); + if (Strict) + await context.WriteAsync("strict "); + await context.WriteAsync(Directed ? "digraph " : "graph "); + await CompileBodyAsync(context); + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Core/DotIdentifier.cs b/Sources/DotNetGraph/Core/DotIdentifier.cs new file mode 100644 index 0000000..816b932 --- /dev/null +++ b/Sources/DotNetGraph/Core/DotIdentifier.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using DotNetGraph.Compilation; +using DotNetGraph.Extensions; + +namespace DotNetGraph.Core +{ + public class DotIdentifier : IDotElement + { + public string Value { get; set; } + + public bool IsHtml { get; set; } = false; + + public DotIdentifier(string value, bool isHtml = false) + { + Value = value; + IsHtml = isHtml; + } + + public async Task CompileAsync(CompilationContext context) + { + if (IsHtml) + { + await context.TextWriter.WriteAsync($"<{Value}>"); + return; + } + + var value = context.Options.AutomaticEscapedCharactersFormat ? Value.FormatGraphvizEscapedCharacters() : Value; + await context.TextWriter.WriteAsync($"\"{value}\""); + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Core/DotNode.cs b/Sources/DotNetGraph/Core/DotNode.cs new file mode 100644 index 0000000..f9fcde0 --- /dev/null +++ b/Sources/DotNetGraph/Core/DotNode.cs @@ -0,0 +1,79 @@ +using System.Linq; +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; + +namespace DotNetGraph.Core +{ + public class DotNode : DotElement + { + public DotIdentifier Identifier { get; set; } + + public DotColorAttribute Color + { + get => GetAttributeOrDefault("color"); + set => SetAttribute("color", value); + } + + public DotColorAttribute FillColor + { + get => GetAttributeOrDefault("fillcolor"); + set => SetAttribute("fillcolor", value); + } + + public DotNodeShapeAttribute Shape + { + get => GetAttributeOrDefault("shape"); + set => SetAttribute("shape", value); + } + + public DotNodeStyleAttribute Style + { + get => GetAttributeOrDefault("style"); + set => SetAttribute("style", value); + } + + public DotDoubleAttribute Width + { + get => GetAttribute("width"); + set => SetAttribute("width", value); + } + + public DotDoubleAttribute Height + { + get => GetAttribute("height"); + set => SetAttribute("height", value); + } + + public DotDoubleAttribute PenWidth + { + get => GetAttribute("penwidth"); + set => SetAttribute("penwidth", value); + } + + public DotPointAttribute Pos + { + get => GetAttribute("pos"); + set => SetAttribute("pos", value); + } + + public override async Task CompileAsync(CompilationContext context) + { + await context.WriteIndentationAsync(); + await Identifier.CompileAsync(context); + if (Attributes.Any()) + { + await context.WriteLineAsync(" ["); + context.IndentationLevel++; + await CompileAttributesAsync(context); + context.IndentationLevel--; + await context.WriteIndentationAsync(); + await context.WriteLineAsync("]"); + } + else + { + await context.WriteLineAsync(); + } + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Node/DotNodeShape.cs b/Sources/DotNetGraph/Core/DotNodeShape.cs similarity index 97% rename from Sources/DotNetGraph/Node/DotNodeShape.cs rename to Sources/DotNetGraph/Core/DotNodeShape.cs index 1fefece..2087692 100644 --- a/Sources/DotNetGraph/Node/DotNodeShape.cs +++ b/Sources/DotNetGraph/Core/DotNodeShape.cs @@ -1,4 +1,4 @@ -namespace DotNetGraph.Node +namespace DotNetGraph.Core { public enum DotNodeShape { diff --git a/Sources/DotNetGraph/Node/DotNodeStyle.cs b/Sources/DotNetGraph/Core/DotNodeStyle.cs similarity index 77% rename from Sources/DotNetGraph/Node/DotNodeStyle.cs rename to Sources/DotNetGraph/Core/DotNodeStyle.cs index 1867f6a..e45e4be 100644 --- a/Sources/DotNetGraph/Node/DotNodeStyle.cs +++ b/Sources/DotNetGraph/Core/DotNodeStyle.cs @@ -1,6 +1,6 @@ using System; -namespace DotNetGraph.Node +namespace DotNetGraph.Core { [Flags] public enum DotNodeStyle @@ -13,6 +13,7 @@ public enum DotNodeStyle Diagonals = 32, Filled = 64, Striped = 128, - Wedged = 256 + Wedged = 256, + Invis = 512 } } \ No newline at end of file diff --git a/Sources/DotNetGraph/Core/DotPoint.cs b/Sources/DotNetGraph/Core/DotPoint.cs new file mode 100644 index 0000000..40b5a09 --- /dev/null +++ b/Sources/DotNetGraph/Core/DotPoint.cs @@ -0,0 +1,39 @@ +namespace DotNetGraph.Core +{ + public struct DotPoint + { + public int X; + + public int Y; + + public int? Z; + + public bool Fixed; + + public DotPoint(int x, int y, bool @fixed = false) + { + X = x; + Y = y; + Z = null; + Fixed = @fixed; + } + + public DotPoint(int x, int y, int z, bool @fixed = false) + { + X = x; + Y = y; + Z = z; + Fixed = @fixed; + } + + public override string ToString() + { + var value = $"{X},{Y}"; + if (Z != null) + value += $",{Z}"; + if (Fixed) + value += "!"; + return value; + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Core/DotPosition.cs b/Sources/DotNetGraph/Core/DotPosition.cs deleted file mode 100644 index 69f6ec1..0000000 --- a/Sources/DotNetGraph/Core/DotPosition.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace DotNetGraph.Core -{ - public class DotPosition - { - public int X { get; set; } - - public int Y { get; set; } - - public DotPosition() - { - } - - public DotPosition(int x, int y) - { - X = x; - Y = y; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Core/DotRankDir.cs b/Sources/DotNetGraph/Core/DotRankDir.cs new file mode 100644 index 0000000..249c041 --- /dev/null +++ b/Sources/DotNetGraph/Core/DotRankDir.cs @@ -0,0 +1,11 @@ +// ReSharper disable InconsistentNaming +namespace DotNetGraph.Core +{ + public enum DotRankDir + { + TB = 0, + BT = 1, + LR = 2, + RL = 3 + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Core/DotSubgraph.cs b/Sources/DotNetGraph/Core/DotSubgraph.cs new file mode 100644 index 0000000..fefb6ee --- /dev/null +++ b/Sources/DotNetGraph/Core/DotSubgraph.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using DotNetGraph.Attributes; +using DotNetGraph.Compilation; + +namespace DotNetGraph.Core +{ + public class DotSubgraph : DotBaseGraph + { + public DotColorAttribute Color + { + get => GetAttributeOrDefault("color"); + set => SetAttribute("color", value); + } + + public DotSubgraphStyleAttribute Style + { + get => GetAttributeOrDefault("style"); + set => SetAttribute("style", value); + } + + public override async Task CompileAsync(CompilationContext context) + { + await context.WriteIndentationAsync(); + await context.WriteAsync("subgraph "); + await CompileBodyAsync(context); + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/SubGraph/DotSubGraphStyle.cs b/Sources/DotNetGraph/Core/DotSubgraphStyle.cs similarity index 61% rename from Sources/DotNetGraph/SubGraph/DotSubGraphStyle.cs rename to Sources/DotNetGraph/Core/DotSubgraphStyle.cs index e311931..03bd885 100644 --- a/Sources/DotNetGraph/SubGraph/DotSubGraphStyle.cs +++ b/Sources/DotNetGraph/Core/DotSubgraphStyle.cs @@ -1,9 +1,9 @@ using System; -namespace DotNetGraph.SubGraph +namespace DotNetGraph.Core { [Flags] - public enum DotSubGraphStyle + public enum DotSubgraphStyle { Solid = 1, Dashed = 2, @@ -11,6 +11,7 @@ public enum DotSubGraphStyle Bold = 8, Rounded = 16, Filled = 32, - Striped = 64 + Striped = 64, + Invis = 128 } } \ No newline at end of file diff --git a/Sources/DotNetGraph/Core/IDotAttribute.cs b/Sources/DotNetGraph/Core/IDotAttribute.cs deleted file mode 100644 index b000d85..0000000 --- a/Sources/DotNetGraph/Core/IDotAttribute.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace DotNetGraph.Core -{ - public interface IDotAttribute - { - - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Core/IDotElement.cs b/Sources/DotNetGraph/Core/IDotElement.cs index 997ba3b..d42eb0c 100644 --- a/Sources/DotNetGraph/Core/IDotElement.cs +++ b/Sources/DotNetGraph/Core/IDotElement.cs @@ -1,6 +1,10 @@ +using System.Threading.Tasks; +using DotNetGraph.Compilation; + namespace DotNetGraph.Core { public interface IDotElement { + Task CompileAsync(CompilationContext context); } } \ No newline at end of file diff --git a/Sources/DotNetGraph/DotGraph.cs b/Sources/DotNetGraph/DotGraph.cs deleted file mode 100644 index fe0e309..0000000 --- a/Sources/DotNetGraph/DotGraph.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; -using DotNetGraph.Core; - -namespace DotNetGraph -{ - public class DotGraph : IDotElement, IDotGraph - { - public bool Directed { get; set; } - - public string Identifier { get; set; } - - public bool Strict { get; set; } - - public List Elements { get; } - - public DotGraph() - { - Elements = new List(); - } - - public DotGraph(string identifier, bool directed = false) : this() - { - Identifier = identifier; - Directed = directed; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/DotNetGraph.csproj b/Sources/DotNetGraph/DotNetGraph.csproj index 04cfe75..cbcf340 100644 --- a/Sources/DotNetGraph/DotNetGraph.csproj +++ b/Sources/DotNetGraph/DotNetGraph.csproj @@ -1,13 +1,13 @@ - - netstandard2.0 - + + netstandard2.0 + - - - <_Parameter1>DotNetGraph.Tests - - + + + <_Parameter1>DotNetGraph.Tests + + diff --git a/Sources/DotNetGraph/DotNetGraph.nuspec b/Sources/DotNetGraph/DotNetGraph.nuspec index 64daadb..0c156c3 100644 --- a/Sources/DotNetGraph/DotNetGraph.nuspec +++ b/Sources/DotNetGraph/DotNetGraph.nuspec @@ -3,16 +3,17 @@ xmlns="https://raw.githubusercontent.com/NuGet/NuGet.Client/dev/src/NuGet.Core/NuGet.Packaging/compiler/resources/nuspec.xsd"> DotNetGraph - 2.7.0 DotNetGraph + 3.0.0 VFRZ VFRZ + icon.png https://github.com/vfrz/DotNetGraph - Create GraphViz DOT diagram / graph using C#/.Net - Custom attributes, extension methods and more. - Copyright 2022 - DOT Graphviz + Create Graphviz DOT graph with dotnet + Initial 3.0 release + Copyright 2023 + DOT Graphviz Graph Visualization Graphs true LICENSE.txt netstandard2.0 @@ -23,5 +24,6 @@ + \ No newline at end of file diff --git a/Sources/DotNetGraph/DotString.cs b/Sources/DotNetGraph/DotString.cs deleted file mode 100644 index c999820..0000000 --- a/Sources/DotNetGraph/DotString.cs +++ /dev/null @@ -1,14 +0,0 @@ -using DotNetGraph.Core; - -namespace DotNetGraph -{ - public class DotString : IDotElement - { - public string Value { get; set; } - - public DotString(string value = null) - { - Value = value; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Edge/DotEdge.cs b/Sources/DotNetGraph/Edge/DotEdge.cs deleted file mode 100644 index 0c6d182..0000000 --- a/Sources/DotNetGraph/Edge/DotEdge.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Drawing; -using DotNetGraph.Attributes; -using DotNetGraph.Core; - -namespace DotNetGraph.Edge -{ - public class DotEdge : DotElementWithAttributes - { - private IDotElement _left; - private IDotElement _right; - - public IDotElement Left - { - get => _left; - set => _left = value ?? throw new ArgumentNullException(nameof(value)); - } - - public IDotElement Right - { - get => _right; - set => _right = value ?? throw new ArgumentNullException(nameof(value)); - } - - public DotColorAttribute Color - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotFontColorAttribute FontColor - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotEdgeStyleAttribute Style - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotLabelAttribute Label - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotPenWidthAttribute PenWidth - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotEdgeArrowHeadAttribute ArrowHead - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotEdgeArrowTailAttribute ArrowTail - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotPositionAttribute Position - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotEdge(IDotElement left, IDotElement right) - { - Left = left ?? throw new ArgumentNullException(nameof(left)); - Right = right ?? throw new ArgumentNullException(nameof(right)); - } - - public DotEdge(string left, string right) - { - if (left == null) - throw new ArgumentNullException(nameof(left)); - - if (right == null) - throw new ArgumentNullException(nameof(right)); - - if (string.IsNullOrWhiteSpace(left)) - throw new ArgumentException("Node cannot be empty", nameof(left)); - - if (string.IsNullOrWhiteSpace(right)) - throw new ArgumentException("Node cannot be empty", nameof(right)); - - Left = new DotString(left); - Right = new DotString(right); - } - - public DotEdge SetCustomAttribute(string name, string value) - { - SetCustomAttributeInternal(name, value); - - return this; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Extensions/ColorExtensions.cs b/Sources/DotNetGraph/Extensions/ColorExtensions.cs new file mode 100644 index 0000000..e404c90 --- /dev/null +++ b/Sources/DotNetGraph/Extensions/ColorExtensions.cs @@ -0,0 +1,14 @@ +using System.Drawing; + +namespace DotNetGraph.Extensions +{ + public static class ColorExtensions + { + public static string ToHexString(this Color color) + { + if (color.A == 255) + return $"#{color.R:X2}{color.G:X2}{color.B:X2}"; + return $"#{color.R:X2}{color.G:X2}{color.B:X2}{color.A:X2}"; + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Extensions/DotBaseGraphExtensions.cs b/Sources/DotNetGraph/Extensions/DotBaseGraphExtensions.cs new file mode 100644 index 0000000..c4eac4c --- /dev/null +++ b/Sources/DotNetGraph/Extensions/DotBaseGraphExtensions.cs @@ -0,0 +1,25 @@ +using DotNetGraph.Core; + +namespace DotNetGraph.Extensions +{ + public static class DotBaseGraphExtensions + { + public static T WithIdentifier(this T graph, string identifier, bool isHtml = false) where T : DotBaseGraph + { + graph.Identifier = new DotIdentifier(identifier, isHtml); + return graph; + } + + public static T WithRankDir(this T graph, DotRankDir? rankDir) where T : DotBaseGraph + { + graph.RankDir = rankDir; + return graph; + } + + public static T Add(this T graph, IDotElement element) where T : DotBaseGraph + { + graph.Elements.Add(element); + return graph; + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Extensions/DotColorExtensions.cs b/Sources/DotNetGraph/Extensions/DotColorExtensions.cs deleted file mode 100644 index 8a12f34..0000000 --- a/Sources/DotNetGraph/Extensions/DotColorExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using DotNetGraph.Attributes; - -namespace DotNetGraph.Extensions -{ - public static class DotColorExtensions - { - public static string ToHex(this DotColorAttribute colorAttribute) - { - return $"#{colorAttribute.Color.R:X2}{colorAttribute.Color.G:X2}{colorAttribute.Color.B:X2}"; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/Extensions/DotEdgeExtensions.cs b/Sources/DotNetGraph/Extensions/DotEdgeExtensions.cs new file mode 100644 index 0000000..3a0a999 --- /dev/null +++ b/Sources/DotNetGraph/Extensions/DotEdgeExtensions.cs @@ -0,0 +1,105 @@ +using System.Drawing; +using DotNetGraph.Attributes; +using DotNetGraph.Core; + +namespace DotNetGraph.Extensions +{ + public static class DotEdgeExtensions + { + public static DotEdge From(this DotEdge edge, string from, bool isHtml = false) + { + edge.From = new DotIdentifier(from, isHtml); + return edge; + } + + public static DotEdge From(this DotEdge edge, DotNode from) + { + edge.From = from.Identifier; + return edge; + } + + public static DotEdge To(this DotEdge edge, string to, bool isHtml = false) + { + edge.To = new DotIdentifier(to, isHtml); + return edge; + } + + public static DotEdge To(this DotEdge edge, DotNode to) + { + edge.To = to.Identifier; + return edge; + } + + public static DotEdge WithColor(this DotEdge edge, string color) + { + edge.Color = new DotColorAttribute(color); + return edge; + } + + public static DotEdge WithColor(this DotEdge edge, Color color) + { + edge.Color = new DotColorAttribute(color); + return edge; + } + + public static DotEdge WithStyle(this DotEdge edge, string style) + { + edge.Style = new DotEdgeStyleAttribute(style); + return edge; + } + + public static DotEdge WithStyle(this DotEdge edge, DotEdgeStyle style) + { + edge.Style = new DotEdgeStyleAttribute(style); + return edge; + } + + public static DotEdge WithPenWidth(this DotEdge edge, double width) + { + edge.PenWidth = new DotDoubleAttribute(width); + return edge; + } + + public static DotEdge WithArrowHead(this DotEdge edge, string arrowType) + { + edge.ArrowHead = new DotEdgeArrowTypeAttribute(arrowType); + return edge; + } + + public static DotEdge WithArrowHead(this DotEdge edge, DotEdgeArrowType arrowType) + { + edge.ArrowHead = new DotEdgeArrowTypeAttribute(arrowType); + return edge; + } + + public static DotEdge WithArrowTail(this DotEdge edge, string arrowType) + { + edge.ArrowTail = new DotEdgeArrowTypeAttribute(arrowType); + return edge; + } + + public static DotEdge WithArrowTail(this DotEdge edge, DotEdgeArrowType arrowType) + { + edge.ArrowTail = new DotEdgeArrowTypeAttribute(arrowType); + return edge; + } + + public static DotEdge WithPos(this DotEdge edge, string value) + { + edge.Pos = new DotPointAttribute(value); + return edge; + } + + public static DotEdge WithPos(this DotEdge edge, int x, int y, bool @fixed = false) + { + edge.Pos = new DotPointAttribute(new DotPoint(x, y, @fixed)); + return edge; + } + + public static DotEdge WithPos(this DotEdge edge, int x, int y, int z, bool @fixed = false) + { + edge.Pos = new DotPointAttribute(new DotPoint(x, y, z, @fixed)); + return edge; + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Extensions/DotElementExtensions.cs b/Sources/DotNetGraph/Extensions/DotElementExtensions.cs new file mode 100644 index 0000000..48fcde7 --- /dev/null +++ b/Sources/DotNetGraph/Extensions/DotElementExtensions.cs @@ -0,0 +1,39 @@ +using System.Drawing; +using DotNetGraph.Attributes; +using DotNetGraph.Core; + +namespace DotNetGraph.Extensions +{ + public static class DotElementExtensions + { + public static T WithAttribute(this T element, string name, IDotAttribute attribute) where T : DotElement + { + element.SetAttribute(name, attribute); + return element; + } + + public static T WithAttribute(this T element, string name, string value) where T : DotElement + { + element.SetAttribute(name, new DotAttribute(value)); + return element; + } + + public static T WithLabel(this T element, string label, bool isHtml = false) where T : DotElement + { + element.Label = new DotLabelAttribute(label, isHtml); + return element; + } + + public static T WithFontColor(this T element, string color) where T : DotElement + { + element.FontColor = new DotColorAttribute(color); + return element; + } + + public static T WithFontColor(this T element, Color color) where T : DotElement + { + element.FontColor = new DotColorAttribute(color); + return element; + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Extensions/DotGraphExtensions.cs b/Sources/DotNetGraph/Extensions/DotGraphExtensions.cs index 6aa7316..c3a300c 100644 --- a/Sources/DotNetGraph/Extensions/DotGraphExtensions.cs +++ b/Sources/DotNetGraph/Extensions/DotGraphExtensions.cs @@ -1,105 +1,18 @@ -using DotNetGraph.Compiler; using DotNetGraph.Core; -using DotNetGraph.Edge; -using DotNetGraph.Node; -using DotNetGraph.SubGraph; -using System; -using System.IO; namespace DotNetGraph.Extensions { public static class DotGraphExtensions { - public static string Compile(this DotGraph graph, bool indented = false, bool formatStrings = true) + public static DotGraph Directed(this DotGraph graph, bool directed = true) { - return new DotCompiler(graph).Compile(indented, formatStrings); - } - - public static void Compile(this DotGraph graph, Stream stream, bool indented = false, bool formatStrings = true) - { - new DotCompiler(graph).Compile(stream, indented, formatStrings); - } - - public static void Compile(this DotGraph graph, TextWriter writer, bool indented = false, bool formatStrings = true) - { - new DotCompiler(graph).Compile(writer, indented, formatStrings); - } - - public static T AddNode(this T graph, string identifier, Action nodeSetup = null) - where T : IDotGraph - { - if (graph == null) - { - throw new ArgumentNullException(nameof(graph)); - } - - var node = new DotNode(identifier); - graph.Elements.Add(node); - - nodeSetup?.Invoke(node); - return graph; - } - - public static T AddEdge(this T graph, IDotElement left, IDotElement right, Action edgeSetup = null) - where T : IDotGraph - { - if (graph == null) - { - throw new ArgumentNullException(nameof(graph)); - } - - var edge = new DotEdge(left, right); - graph.Elements.Add(edge); - - edgeSetup?.Invoke(edge); - return graph; - } - - public static T AddEdge(this T graph, string left, string right, Action edgeSetup = null) - where T : IDotGraph - { - if (graph == null) - { - throw new ArgumentNullException(nameof(graph)); - } - - var edge = new DotEdge(left, right); - graph.Elements.Add(edge); - - edgeSetup?.Invoke(edge); - return graph; - } - - public static T AddSubGraph(this T graph, string identifier, Action subGraphSetup = null) - where T : IDotGraph - { - if (graph == null) - { - throw new ArgumentNullException(nameof(graph)); - } - - var subGraph = new DotSubGraph(identifier); - graph.Elements.Add(subGraph); - - subGraphSetup?.Invoke(subGraph); + graph.Directed = directed; return graph; } - - public static T AddLine(this T graph, string rawLine) - where T : IDotGraph + + public static DotGraph Strict(this DotGraph graph, bool strict = true) { - if (graph == null) - { - throw new ArgumentNullException(nameof(graph)); - } - if (string.IsNullOrWhiteSpace(rawLine)) - { - throw new ArgumentException("Line cannot be empty", nameof(rawLine)); - } - - var stringElement = new DotString(rawLine); - graph.Elements.Add(stringElement); - + graph.Strict = strict; return graph; } } diff --git a/Sources/DotNetGraph/Extensions/DotNodeExtensions.cs b/Sources/DotNetGraph/Extensions/DotNodeExtensions.cs new file mode 100644 index 0000000..759a7b1 --- /dev/null +++ b/Sources/DotNetGraph/Extensions/DotNodeExtensions.cs @@ -0,0 +1,99 @@ +using System.Drawing; +using DotNetGraph.Attributes; +using DotNetGraph.Core; + +namespace DotNetGraph.Extensions +{ + public static class DotNodeExtensions + { + public static DotNode WithIdentifier(this DotNode node, string identifier, bool isHtml = false) + { + node.Identifier = new DotIdentifier(identifier, isHtml); + return node; + } + + public static DotNode WithColor(this DotNode node, string color) + { + node.Color = new DotColorAttribute(color); + return node; + } + + public static DotNode WithColor(this DotNode node, Color color) + { + node.Color = new DotColorAttribute(color); + return node; + } + + public static DotNode WithFillColor(this DotNode node, string color) + { + node.FillColor = new DotColorAttribute(color); + return node; + } + + public static DotNode WithFillColor(this DotNode node, Color color) + { + node.FillColor = new DotColorAttribute(color); + return node; + } + + public static DotNode WithShape(this DotNode node, string shape) + { + node.Shape = new DotNodeShapeAttribute(shape); + return node; + } + + public static DotNode WithShape(this DotNode node, DotNodeShape shape) + { + node.Shape = new DotNodeShapeAttribute(shape); + return node; + } + + public static DotNode WithStyle(this DotNode node, string style) + { + node.Style = new DotNodeStyleAttribute(style); + return node; + } + + public static DotNode WithStyle(this DotNode node, DotNodeStyle style) + { + node.Style = new DotNodeStyleAttribute(style); + return node; + } + + public static DotNode WithWidth(this DotNode node, double width) + { + node.Width = new DotDoubleAttribute(width); + return node; + } + + public static DotNode WithHeight(this DotNode node, double height) + { + node.Height = new DotDoubleAttribute(height); + return node; + } + + public static DotNode WithPenWidth(this DotNode node, double width) + { + node.PenWidth = new DotDoubleAttribute(width); + return node; + } + + public static DotNode WithPos(this DotNode node, string value) + { + node.Pos = new DotPointAttribute(value); + return node; + } + + public static DotNode WithPos(this DotNode node, int x, int y, bool @fixed = false) + { + node.Pos = new DotPointAttribute(new DotPoint(x, y, @fixed)); + return node; + } + + public static DotNode WithPos(this DotNode node, int x, int y, int z, bool @fixed = false) + { + node.Pos = new DotPointAttribute(new DotPoint(x, y, z, @fixed)); + return node; + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Extensions/DotSubgraphExtensions.cs b/Sources/DotNetGraph/Extensions/DotSubgraphExtensions.cs new file mode 100644 index 0000000..e0f33e0 --- /dev/null +++ b/Sources/DotNetGraph/Extensions/DotSubgraphExtensions.cs @@ -0,0 +1,33 @@ +using System.Drawing; +using DotNetGraph.Attributes; +using DotNetGraph.Core; + +namespace DotNetGraph.Extensions +{ + public static class DotSubgraphExtensions + { + public static DotSubgraph WithColor(this DotSubgraph subgraph, string color) + { + subgraph.Color = new DotColorAttribute(color); + return subgraph; + } + + public static DotSubgraph WithColor(this DotSubgraph subgraph, Color color) + { + subgraph.Color = new DotColorAttribute(color); + return subgraph; + } + + public static DotSubgraph WithStyle(this DotSubgraph subgraph, string style) + { + subgraph.Style = new DotSubgraphStyleAttribute(style); + return subgraph; + } + + public static DotSubgraph WithStyle(this DotSubgraph subgraph, DotSubgraphStyle style) + { + subgraph.Style = new DotSubgraphStyleAttribute(style); + return subgraph; + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Extensions/EnumExtensions.cs b/Sources/DotNetGraph/Extensions/EnumExtensions.cs index 211e716..0c3ac6f 100644 --- a/Sources/DotNetGraph/Extensions/EnumExtensions.cs +++ b/Sources/DotNetGraph/Extensions/EnumExtensions.cs @@ -1,23 +1,20 @@ -using System; +using System; using System.Linq; using System.Reflection; namespace DotNetGraph.Extensions { - internal static class EnumExtensions + public static class EnumExtensions { - public static string FlagsToString(this T @enum) where T : Enum + public static string FlagsToString(this T enumValue) where T : Enum { - var type = typeof(T); - if (type.GetCustomAttribute() == null) - { - throw new InvalidOperationException($"The type '{type}' doesn't have the [Flags] attribute specified."); - } + if (typeof(T).GetCustomAttribute() is null) + throw new InvalidOperationException($"The type '{typeof(T)}' doesn't have the [Flags] attribute specified."); - return string.Join(",", Enum.GetValues(type) + return string.Join(",", Enum.GetValues(typeof(T)) .Cast() - .Where(a => @enum.HasFlag(a)) + .Where(a => enumValue.HasFlag(a)) .Select(a => a.ToString().ToLowerInvariant())); } } -} +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Extensions/StringExtensions.cs b/Sources/DotNetGraph/Extensions/StringExtensions.cs new file mode 100644 index 0000000..c3f2322 --- /dev/null +++ b/Sources/DotNetGraph/Extensions/StringExtensions.cs @@ -0,0 +1,13 @@ +namespace DotNetGraph.Extensions +{ + public static class StringExtensions + { + internal static string FormatGraphvizEscapedCharacters(this string value) + { + return value?.Replace("\\", "\\\\") + .Replace("\"", "\\\"") + .Replace("\r\n", "\\n") + .Replace("\n", "\\n"); + } + } +} \ No newline at end of file diff --git a/Sources/DotNetGraph/Extensions/TextWriterExtensions.cs b/Sources/DotNetGraph/Extensions/TextWriterExtensions.cs deleted file mode 100644 index 02b8119..0000000 --- a/Sources/DotNetGraph/Extensions/TextWriterExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.IO; - -namespace DotNetGraph.Extensions -{ - internal static class TextWriterExtensions - { - public static TextWriter AddIndentation(this TextWriter writer, bool indented, int indentationLevel) - { - if (writer == null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (!indented) - return writer; - - for (var i = 0; i < indentationLevel; i++) - { - writer.Write("\t"); - } - - return writer; - } - - public static TextWriter AddIndentationNewLine(this TextWriter writer, bool indented) - { - if (writer == null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (indented) - { - writer.Write("\n"); - } - - return writer; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/IDotGraph.cs b/Sources/DotNetGraph/IDotGraph.cs deleted file mode 100644 index a88f922..0000000 --- a/Sources/DotNetGraph/IDotGraph.cs +++ /dev/null @@ -1,11 +0,0 @@ -using DotNetGraph.Core; -using System.Collections.Generic; - -namespace DotNetGraph -{ - public interface IDotGraph - { - List Elements { get; } - string Identifier { get; set; } - } -} diff --git a/Sources/DotNetGraph/Node/DotNode.cs b/Sources/DotNetGraph/Node/DotNode.cs deleted file mode 100644 index 8adb998..0000000 --- a/Sources/DotNetGraph/Node/DotNode.cs +++ /dev/null @@ -1,105 +0,0 @@ -using DotNetGraph.Attributes; -using DotNetGraph.Core; -using System; - -namespace DotNetGraph.Node -{ - public class DotNode : DotElementWithAttributes - { - private string _identifier; - - public string Identifier - { - get => _identifier; - set - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - if (string.IsNullOrWhiteSpace(value)) - throw new ArgumentException("Identifier cannot be empty", nameof(value)); - - _identifier = value; - } - } - - public DotColorAttribute Color - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotFontColorAttribute FontColor - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotFillColorAttribute FillColor - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotNodeShapeAttribute Shape - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotNodeStyleAttribute Style - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotLabelAttribute Label - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotNodeWidthAttribute Width - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotPenWidthAttribute PenWidth - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotNodeHeightAttribute Height - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotPositionAttribute Position - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotNode(string identifier, DotColorAttribute color = null) - { - if (identifier == null) - throw new ArgumentNullException(nameof(identifier)); - - if (string.IsNullOrWhiteSpace(identifier)) - throw new ArgumentException("Identifier cannot be empty", nameof(identifier)); - - Identifier = identifier; - Color = color; - } - - public DotNode SetCustomAttribute(string name, string value) - { - SetCustomAttributeInternal(name, value); - - return this; - } - } -} \ No newline at end of file diff --git a/Sources/DotNetGraph/SubGraph/DotSubGraph.cs b/Sources/DotNetGraph/SubGraph/DotSubGraph.cs deleted file mode 100644 index 5fc95f4..0000000 --- a/Sources/DotNetGraph/SubGraph/DotSubGraph.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Collections.Generic; -using DotNetGraph.Attributes; -using DotNetGraph.Core; - -namespace DotNetGraph.SubGraph -{ - public class DotSubGraph : DotElementWithAttributes, IDotGraph - { - public string Identifier { get; set; } - - public DotColorAttribute Color - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotSubGraphStyleAttribute Style - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public DotLabelAttribute Label - { - get => GetAttribute(); - set => SetAttribute(value); - } - - public List Elements { get; } - - public DotSubGraph(string identifier = null) - { - Elements = new List(); - Identifier = identifier; - } - - public DotSubGraph SetCustomAttribute(string name, string value) - { - SetCustomAttributeInternal(name, value); - - return this; - } - } -} \ No newline at end of file