Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 6 additions & 23 deletions ArchUnitNET/Domain/PlantUml/Export/PlantUmlDependency.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,24 +51,13 @@ public string GetPlantUmlString(RenderOptions renderOptions = null)
+ Environment.NewLine;

case DependencyType.OneToPackage:
return "["
+ Origin
+ "] -[#red]> "
+ GetChildNamespace(Target)
+ Environment.NewLine;
return "[" + Origin + "] -[#red]> " + Target + Environment.NewLine;

case DependencyType.PackageToOne:
return GetChildNamespace(Origin)
+ " -[#blue]> ["
+ Target
+ "]"
+ Environment.NewLine;
return Origin + " -[#blue]> [" + Target + "]" + Environment.NewLine;

case DependencyType.PackageToPackage:
return GetChildNamespace(Origin)
+ " -[#green]> "
+ GetChildNamespace(Target)
+ Environment.NewLine;
return Origin + " -[#green]> " + Target + Environment.NewLine;

case DependencyType.OneToOneCompact:
if (OriginCountOfDots() == TargetCountOfDots())
Expand All @@ -93,10 +82,7 @@ public string GetPlantUmlString(RenderOptions renderOptions = null)
&& (OriginCountOfDots() == 0 || HaveSameParentNamespace(Origin, Target))
)
{
return GetChildNamespace(Origin)
+ " ..> "
+ GetChildNamespace(Target)
+ Environment.NewLine;
return Origin + " ..> " + Target + Environment.NewLine;
}
return "";

Expand All @@ -119,7 +105,7 @@ public string GetPlantUmlString(RenderOptions renderOptions = null)

if (tmp != Origin && HaveSameParentNamespace(tmp, Origin))
{
return Origin + " --> " + GetChildNamespace(tmp) + Environment.NewLine;
return Origin + " --> " + tmp + Environment.NewLine;
}
}
else
Expand All @@ -132,7 +118,7 @@ public string GetPlantUmlString(RenderOptions renderOptions = null)

if (tmp != Target && HaveSameParentNamespace(tmp, Target))
{
return GetChildNamespace(tmp) + " -> " + Target + Environment.NewLine;
return tmp + " -> " + Target + Environment.NewLine;
}
}
return "";
Expand All @@ -147,9 +133,6 @@ public string GetPlantUmlString(RenderOptions renderOptions = null)
private static string GetParentNamespace(string ns) =>
ns.Remove(ns.LastIndexOf(".", StringComparison.Ordinal));

private static string GetChildNamespace(string ns) =>
ns.Remove(0, ns.LastIndexOf(".", StringComparison.Ordinal) + 1);

private static bool HaveSameParentNamespace(string origin, string target) =>
(GetParentNamespace(origin) == GetParentNamespace(target));

Expand Down
52 changes: 40 additions & 12 deletions ArchUnitNET/Domain/PlantUml/Export/PlantUmlDiagram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,48 @@ public string GetPlantUmlString(RenderOptions renderOptions)
)
.AppendLine();
result.AppendLine("HIDE_STEREOTYPE()").AppendLine();
result.Append(
PlantUmlElements
.OrderBy(element => element.GetType() != typeof(PlantUmlNamespace))
.ThenBy(element => element.GetType() != typeof(PlantUmlSlice))
.ThenBy(element => element.GetType() != typeof(PlantUmlClass))
.ThenBy(element => element.GetType() != typeof(PlantUmlInterface))
.Aggregate(
"",
(umlString, umlElement) =>
umlString + umlElement.GetPlantUmlString(renderOptions)
)
);
var orderedElements = PlantUmlElements
.OrderBy(element => element.GetType() != typeof(PlantUmlNamespace))
.ThenBy(element => element.GetType() != typeof(PlantUmlSlice))
.ThenBy(element => element.GetType() != typeof(PlantUmlClass))
.ThenBy(element => element.GetType() != typeof(PlantUmlInterface));
foreach (var element in MergeNestedSlices(orderedElements))
{
result.Append(element.GetPlantUmlString(renderOptions));
}
result.AppendLine("@enduml");
return result.ToString();
}

/// <summary>
/// Replaces all nested slices that share a root package with a single tree for that
/// package, placed where its first slice was. All other elements keep their position.
/// </summary>
private static IEnumerable<IPlantUmlElement> MergeNestedSlices(
IEnumerable<IPlantUmlElement> elements
)
{
var mergedElements = new List<IPlantUmlElement>();
var sliceTrees = new Dictionary<string, PlantUmlSliceTree>();
foreach (var element in elements)
{
if (!(element is PlantUmlSlice slice) || !slice.IsNested)
{
mergedElements.Add(element);
}
else if (sliceTrees.TryGetValue(slice.RootPackage, out var tree))
{
tree.Add(slice);
}
else
{
tree = new PlantUmlSliceTree(slice);
sliceTrees.Add(slice.RootPackage, tree);
mergedElements.Add(tree);
}
}

return mergedElements;
}
}
}
139 changes: 63 additions & 76 deletions ArchUnitNET/Domain/PlantUml/Export/PlantUmlSlice.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ArchUnitNET.Domain.PlantUml.Export
Expand Down Expand Up @@ -38,111 +37,99 @@ public void UseS4Style()
C4Style = true;
}

public string GetPlantUmlString(RenderOptions renderOptions)
{
var result = C4Style ? BuildStringC4Style() : BuildString();
internal bool IsNested => Namespace != null;

if (Hyperlink != null)
internal bool IsC4Style => C4Style;

internal string SliceColor => Color;

/// <summary>
/// The outermost package this slice is nested in, which is its whole namespace.
/// </summary>
internal string RootPackage => Namespace.Remove(Namespace.Length - 1);

/// <summary>
/// The packages this slice is nested in, outermost first. The first entry is the whole
/// namespace of the slice, the others are the segments between it and the leaf.
/// </summary>
internal IReadOnlyList<string> PackagePath
{
get
{
result.Append(" [[" + Hyperlink + "]] ");
var path = new List<string> { RootPackage };
var segments = _name.Remove(0, Namespace.Length).Split('.');
path.AddRange(segments.Take(segments.Length - 1));
return path;
}

return result.AppendLine().ToString();
}

private StringBuilder BuildString()
/// <summary>
/// The label of the component inside its innermost package, or an empty string if the
/// slice only stands for that package.
/// </summary>
internal string Leaf
{
var result = new StringBuilder();
if (Namespace != null)
get
{
result.Append("package " + Namespace.Remove(Namespace.Length - 1));
var name = _name.Remove(0, Namespace.Length);
var iter = 1;
while (name.Contains("."))
{
var dotPattern = name.IndexOf(".", StringComparison.Ordinal);
result.AppendLine(" {");
result.Append("package " + name.Remove(dotPattern));
name = name.Remove(0, dotPattern + 1);
iter++;
}
return name.Substring(name.LastIndexOf('.') + 1);
}
}

if (name != "")
{
result.AppendLine(" {");
result.Append("[" + name + "] as " + _name);
if (Color != null)
{
result.AppendLine(" #" + Color);
}
else
{
result.AppendLine();
}
}
else if (Color != null)
{
result.AppendLine(" #" + Color + " {");
}
else
{
result.AppendLine(" {");
}
public string GetPlantUmlString(RenderOptions renderOptions)
{
if (IsNested)
{
return new PlantUmlSliceTree(this).GetPlantUmlString(renderOptions);
}

for (var i = iter; i > 0; i--)
{
result.AppendLine("}");
}
var result = new StringBuilder();
if (C4Style)
{
result.Append("Container(" + _name + ", " + _name + ")");
AppendHyperlink(result);
}
else
{
result.Append("[" + _name + "]");
AppendHyperlink(result);
if (Color != null)
{
result.Append(" #" + Color);
}
}

return result;
return result.AppendLine().ToString();
}

private StringBuilder BuildStringC4Style()
internal string GetLeafString()
{
var result = new StringBuilder();
if (Namespace == null)
if (C4Style)
{
result.Append("Container(" + _name + ", " + _name + ")");
return result;
result.Append("Container(" + _name + ", " + Leaf + ")");
AppendHyperlink(result);
}

var namespc = Namespace.Remove(Namespace.Length - 1);
result.Append("Boundary(" + namespc + ", " + namespc + ") ");
var name = _name.Remove(0, Namespace.Length);
var iter = 1;
while (name.Contains("."))
else
{
var dotPattern = name.IndexOf(".", StringComparison.Ordinal);
result.AppendLine(" {");
result.Append(
"Boundary(" + name.Remove(dotPattern) + ", " + name.Remove(dotPattern) + ") "
);
name = name.Remove(0, dotPattern + 1);
iter++;
result.Append("[" + Leaf + "] as " + _name);
AppendHyperlink(result);
if (Color != null)
{
result.Append(" #" + Color);
}
}

result.AppendLine(" {");
if (name != "")
{
result.Append("Container(" + _name + ", " + name + ")");
result.AppendLine();
}
return result.ToString();
}

for (var i = iter; i > 0; i--)
// PlantUML only accepts a link in front of the color, not after it.
private void AppendHyperlink(StringBuilder result)
{
if (Hyperlink != null)
{
result.AppendLine("}");
result.Append(" [[" + Hyperlink + "]] ");
}

return result;
}
}
}
Loading
Loading