Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove nullable reference type warning #3

Merged
merged 1 commit into from
Jul 5, 2022
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
5 changes: 3 additions & 2 deletions DomainModel.Generator.CLI.Tests/UnitTest1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public void TwoClasses_WithReferenceButInDifferentOrder_ShouldHaveRelation()
[InlineData(new[] { "yyy", "zzz" }, "xxx.yyy", false)]
[InlineData(new[] { "xxx" }, "xxyy", false)]
[InlineData(new[] { "xxx" }, null, false)]
public void Options_WithNamesapces(string[] namespaces, string actualNamespace, bool expectedResult)
public void Options_WithNamespaces(string[] namespaces, string actualNamespace, bool expectedResult)
{
var o = new Options(modulePath: "x", generateOptions: null, namespaces: namespaces);
o.ShouldBe(actualNamespace).Should().Be(expectedResult);
Expand All @@ -99,9 +99,10 @@ public void TestFunction()
{
var node = new Node(typeof(TestClass3));
node.AddPublicAttribute("generic", typeof(List<int>));
node.AddPublicAttribute("openGeneric", typeof(IDictionary<,>));
node.AddPublicAttribute("nullable", typeof(int?));
node["generic"].Should().Be("List<int>");

node["openGeneric"].Should().Be("IDictionary<TKey,TValue>");
node["nullable"].Should().Be("int?");
}

Expand Down
4 changes: 2 additions & 2 deletions DomainModel.Generator.CLI/ModelLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public Graph LoadModule(Options options)
Console.WriteLine($"Loading core assemblies from {runtimeDirectory}");
string[] runtimeAssemblies = Directory.GetFiles(runtimeDirectory, "*.dll");

var moduleAssemblyPath = Path.Combine(Directory.GetCurrentDirectory(), options.ModulePath);
string[] modelAssemblies = Directory.GetFiles(Path.GetDirectoryName(moduleAssemblyPath), "*.dll");
var moduleAssemblyPath = Path.GetFullPath(options.ModulePath);
string[] modelAssemblies = Directory.GetFiles(Path.GetDirectoryName(moduleAssemblyPath)!, "*.dll");
Console.WriteLine($"Following libraries will be scanned: \n{string.Join(Environment.NewLine, modelAssemblies)}");

var paths = runtimeAssemblies.Union(modelAssemblies);
Expand Down
54 changes: 28 additions & 26 deletions DomainModel.Generator.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,28 @@
static int OnError(string usage) { Console.Error.WriteLine(usage); return 1; }
static int Run(IDictionary<string, ArgValue> arguments)
{
if (arguments["generate"].IsTrue)
{
var options = new Options(
generateOptions: new GenerateOptions(
outputPath: (string)arguments["--output"],
diagramType: (string)arguments["--diagram"],
outputFormat: (string)arguments["--format"]),
modulePath: (string)arguments["--module"],
namespaces: ((StringList)arguments["--namespace"]).ToArray());
if (!arguments["generate"].IsTrue)
return 1;

var optionValidator = new OptionsValidator();
optionValidator.AssertOptions(options);
var options = CreateOptionsFrom(arguments);
new OptionsValidator().AssertOptions(options);

var modelLoader = new ModelLoader(
new ModelReflector(options));
var graph = modelLoader.LoadModule(options);
var diagram = GenerateDiagram(graph);
File.WriteAllText(options.GenerateOptions.OutputPath, diagram);
return 0;
}
return 1;
var modelLoader = new ModelLoader(
new ModelReflector(options));
var graph = modelLoader.LoadModule(options);
var diagram = GenerateDiagram(graph);
File.WriteAllText(options.GenerateOptions.OutputPath, diagram);
return 0;
}

static Options CreateOptionsFrom(IDictionary<string, ArgValue> arguments) => new Options(
generateOptions: new GenerateOptions(
outputPath: (string)arguments["--output"],
diagramType: (string)arguments["--diagram"],
outputFormat: (string)arguments["--format"]),
modulePath: (string)arguments["--module"],
namespaces: ((StringList)arguments["--namespace"]).ToArray());

static string GenerateDiagram(Graph graph)
{
var diagramGenerator = new DomainModel.Generator.Mermaid.ClassDiagramGenerator();
Expand All @@ -66,12 +66,14 @@ static string GenerateDiagram(Graph graph)
}
return diagramGenerator.Generate();
}
static Version? GetVersion()
{
return typeof(Options).Assembly.GetName().Version;
}
return Docopt.CreateParser(usage)
.WithVersion("Domain model v0.1")
.WithVersion("Domain model v" + GetVersion())
.Parse(args)
.Match(Run,
result => ShowHelp(result.Help),
result => ShowVersion(result.Version),
result => OnError(result.Usage));


.Match<int>(Run,
(Func<IHelpResult, int>)(result => ShowHelp(result.Help)),
(Func<IVersionResult, int>)(result => ShowVersion(result.Version)),
(Func<IInputErrorResult, int>)(result => OnError(result.Usage)));
42 changes: 21 additions & 21 deletions DomainModel.Generator.CLI/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,32 @@ public static class TypeExtensions
{ typeof(void), "void" }
};

static Type UnknownType = typeof(object);
static string UnknownTypeName = UnknownType.Name;

static private string GetTypeNameOrAlias(Type type)
{

// Handle nullable value types
var nullbase = Nullable.GetUnderlyingType(type);
if (nullbase != null)
return GetTypeNameOrAlias(nullbase) + "?";
var baseType = Nullable.GetUnderlyingType(type);
if (baseType != null)
return GetTypeNameOrAlias(baseType) + "?";

// Handle generic types
if (type.IsGenericType)
{
string name = type.Name.Split('`').FirstOrDefault();
IEnumerable<string> parms =
string name = type.Name.Split('`').FirstOrDefault(s => !string.IsNullOrWhiteSpace(s)) ?? type.Name;
var parameters =
type.GetGenericArguments()
.Select(a => type.IsConstructedGenericType ? GetTypeNameOrAlias(a) : a.Name);
return $"{name}<{string.Join(",", parms)}>";
return $"{name}<{string.Join(",", parameters)}>";
}

// Handle arrays
if (type.BaseType == typeof(System.Array))
return GetTypeNameOrAlias(type.GetElementType()) + "[]";
return GetTypeNameOrAlias(type.GetElementType() ?? UnknownType) + "[]";

// Lookup alias for type
if (_typeAlias.TryGetValue(type, out string alias))
if (_typeAlias.TryGetValue(type, out string? alias))
return alias;

// Default to CLR type name
Expand All @@ -54,25 +56,23 @@ static private string GetTypeNameOrAlias(Type type)
public static string FormatTypeName(this Type type)
{
string name = GetTypeNameOrAlias(type);
if (type.DeclaringType is Type dec)
//If type is defined in another type
if (type.DeclaringType is Type declaringType)
{
return $"{FormatTypeName(dec)}.{name}";
return $"{FormatTypeName(declaringType)}.{name}";
}
return name;
}

public static Type GetClassType(this Type type)
public static Type[] GetContainingTypes(this Type type)
{
var nodeName = type;
if (type.IsArray)
{
nodeName = type.GetElementType();
}
else if (type.IsGenericType && type.FullName.StartsWith("System.Collection"))
{
nodeName = type.GetGenericArguments()[0];
}
return nodeName;
return new[] { type.GetElementType() ?? type };

if (type.IsGenericType && type.IsConstructedGenericType)
return type.GetGenericArguments();

return new[] { type }; ;

}
}
27 changes: 21 additions & 6 deletions DomainModel.Generator.CLI/TypeGraph.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
public class Node
{
private readonly Dictionary<string, (string TypeName, string TypeFullName)> attributes = new();
private readonly Dictionary<string, string> attributes = new();

public Node(Type type)
{
Type = type;
}
public string this[string name] => attributes[name].TypeName;
public string this[string name] => attributes[name];
public string Name => Type.Name;
public (string name, string type)[] Attributes => attributes.Select(a => (a.Key, a.Value.TypeName)).ToArray();
public (string name, string type)[] Attributes => attributes.Select(a => (a.Key, a.Value)).ToArray();

public Type Type { get; }

public void AddPublicAttribute(string name, Type type)
{
attributes.Add(name, (TypeName: type.FormatTypeName(), TypeFullName: type.FullName));
attributes.Add(name, type.FormatTypeName());
}
}
public class Edge
Expand All @@ -36,6 +36,9 @@ public class Graph
public Edge[] Edges => edges.ToArray();
public Node AddNode(Type type)
{
if (type.FullName is null)
throw new ArgumentNullException(nameof(type.FullName));

if (nodes.ContainsKey(type.FullName))
return nodes[type.FullName];

Expand All @@ -44,9 +47,21 @@ public Node AddNode(Type type)
return node;
}

public bool TryGetNodeFor(Type type, out Node node)
public bool TryGetNodeFor(Type type, out Node? node)
{
var typeFullName = type.FullName;
if (string.IsNullOrWhiteSpace(typeFullName))
{
node = null;
return false;
}
return nodes.TryGetValue(typeFullName, out node);
}

public Node[] FindNodes(params Type[] types)
{
return nodes.TryGetValue(type.FullName, out node);
return types.Where(t => t.FullName is not null && nodes.ContainsKey(t.FullName))
.Select(t => nodes[t.FullName!]).ToArray();
}

internal Edge AddEdge(Node from, Node to)
Expand Down
8 changes: 4 additions & 4 deletions DomainModel.Generator.CLI/TypeGraphBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ public Node AddType(Type type)
public void AddPublicAttribute(Node node, string name, Type type)
{
node.AddPublicAttribute(name, type);
if (TryGetConnectedNode(type, out var otherNode))
foreach (var otherNode in TryGetConnectedNodes(type))
{
graph.AddEdge(node, otherNode);
}
}

public Graph Build() => this.graph;

private bool TryGetConnectedNode(Type type, out Node node)
private Node[] TryGetConnectedNodes(Type type)
{
var nodeName = type.GetClassType();
return graph.TryGetNodeFor(nodeName, out node);
var types = type.GetContainingTypes();
return graph.FindNodes(types);
}
}