Skip to content

Commit a4ae9f7

Browse files
authored
Inheritance resolver (#28)
1 parent 31fbd5a commit a4ae9f7

File tree

9 files changed

+409
-57
lines changed

9 files changed

+409
-57
lines changed

src/BitzArt.XDoc/DocumentationElements/DocumentationElement.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,9 @@ protected DocumentationElement(XDoc source, XmlNode? node)
2828
Source = source;
2929
Node = node;
3030
}
31+
32+
/// <summary>
33+
/// Gets the text content of the XML node.
34+
/// </summary>
35+
public string Text => Node?.InnerText.Trim() ?? string.Empty;
3136
}

src/BitzArt.XDoc/DocumentationElements/MemberDocumentation.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public abstract class MemberDocumentation<TMemberInfo> : DocumentationElement, I
1717

1818
TMemberInfo IDocumentationElement<TMemberInfo>.Target => Member;
1919

20-
internal MemberDocumentation(XDoc source, TMemberInfo member, XmlNode node)
20+
internal MemberDocumentation(XDoc source, TMemberInfo member, XmlNode? node)
2121
: base(source, node)
2222
{
2323
Member = member;
@@ -30,20 +30,20 @@ internal MemberDocumentation(XDoc source, TMemberInfo member, XmlNode node)
3030
/// <inheritdoc/>
3131
public sealed class FieldDocumentation : MemberDocumentation<FieldInfo>
3232
{
33-
internal FieldDocumentation(XDoc source, FieldInfo field, XmlNode node)
33+
internal FieldDocumentation(XDoc source, FieldInfo field, XmlNode? node)
3434
: base(source, field, node) { }
3535
}
3636

3737
/// <inheritdoc/>
3838
public sealed class MethodDocumentation : MemberDocumentation<MethodInfo>
3939
{
40-
internal MethodDocumentation(XDoc source, MethodInfo method, XmlNode node)
40+
internal MethodDocumentation(XDoc source, MethodInfo method, XmlNode? node)
4141
: base(source, method, node) { }
4242
}
4343

4444
/// <inheritdoc/>
4545
public sealed class PropertyDocumentation : MemberDocumentation<PropertyInfo>
4646
{
47-
internal PropertyDocumentation(XDoc source, PropertyInfo property, XmlNode node)
47+
internal PropertyDocumentation(XDoc source, PropertyInfo property, XmlNode? node)
4848
: base(source, property, node) { }
4949
}

src/BitzArt.XDoc/DocumentationElements/TypeDocumentation.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,23 @@ namespace BitzArt.XDoc;
66
/// <summary>
77
/// Holds information about documentation of a <see cref="System.Type"/>.
88
/// </summary>
9-
public sealed class TypeDocumentation : DocumentationElement, IDocumentationElement<Type>
9+
public sealed class TypeDocumentation : MemberDocumentation<Type>, IDocumentationElement<Type>
1010
{
1111
private readonly Dictionary<MemberInfo, DocumentationElement> _memberData;
1212

1313
/// <summary>
14-
/// The <see cref="Type"/> this documentation if provided for.
14+
/// The Type this documentation if provided for.
1515
/// </summary>
16-
public Type Type { get; private init; }
17-
18-
Type IDocumentationElement<Type>.Target => Type;
16+
public Type Type => Member;
1917

2018
/// <summary>
2119
/// List of members declared by this <see cref="Type"/>.
2220
/// </summary>
2321
internal IReadOnlyDictionary<MemberInfo, DocumentationElement> MemberData => _memberData.AsReadOnly();
2422

2523
internal TypeDocumentation(XDoc source, Type type, XmlNode? node)
26-
: base(source, node)
24+
: base(source, type, node)
2725
{
28-
Type = type;
29-
3026
_memberData = [];
3127
}
3228

src/BitzArt.XDoc/XDoc.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ public class XDoc
3030
/// <summary>
3131
/// Initializes a new instance of the <see cref="XDoc"/> class.
3232
/// </summary>
33-
public XDoc() : this(new()) { }
33+
public XDoc() : this(new())
34+
{
35+
}
3436

3537
internal XDoc(ConcurrentDictionary<Assembly, AssemblyDocumentation> fetchedAssemblies)
3638
{
@@ -115,6 +117,10 @@ private AssemblyDocumentation Fetch(Assembly assembly)
115117
/// </summary>
116118
/// <param name="memberInfo"></param>
117119
/// <returns></returns>
118-
public DocumentationElement? Get(MemberInfo memberInfo)
119-
=> Get(memberInfo.DeclaringType!)?.GetDocumentation(memberInfo);
120+
public DocumentationElement? Get(MemberInfo memberInfo) =>
121+
memberInfo switch
122+
{
123+
Type type => Get(type),
124+
_ => Get(memberInfo.DeclaringType!)?.GetDocumentation(memberInfo)
125+
};
120126
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace BitzArt.XDoc;
2+
3+
/// <summary>
4+
/// Contains extension methods for <see cref="Type"/>.
5+
/// </summary>
6+
internal static class TypeExtensions
7+
{
8+
public static IEnumerable<Type> GetImmediateInterfaces(this Type type)
9+
{
10+
var inheritedInterfaces = type.GetInterfaces().SelectMany(i => i.GetInterfaces()).ToList();
11+
inheritedInterfaces.AddRange(type.BaseType?.GetInterfaces() ?? []);
12+
13+
return type.GetInterfaces().Except(inheritedInterfaces);
14+
}
15+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
using System.Reflection;
2+
using System.Xml;
3+
4+
namespace BitzArt.XDoc;
5+
6+
/// <summary>
7+
/// Direct inheritance resolver (<c>&lt;inheritdoc/&gt;</c>)
8+
/// </summary>
9+
internal static class InheritanceResolver
10+
{
11+
public static MemberInfo? GetTargetMember(MemberInfo sourceMember, XmlNode? node)
12+
{
13+
return sourceMember switch
14+
{
15+
Type type => FindTargetType(type),
16+
_ => FindTargetMember(sourceMember.DeclaringType!, sourceMember, false)
17+
};
18+
}
19+
20+
private static MemberInfo? FindTargetType(Type type)
21+
{
22+
if (type.BaseType is not null)
23+
{
24+
return type.BaseType;
25+
}
26+
27+
return type.GetImmediateInterfaces().FirstOrDefault();
28+
}
29+
30+
private static MemberInfo? FindTargetMember(Type type, MemberInfo sourceMember, bool checkOwnMembers = false)
31+
{
32+
if (checkOwnMembers)
33+
{
34+
if (CheckPresence(type, sourceMember, out var found))
35+
{
36+
return found;
37+
}
38+
}
39+
40+
if (type.BaseType != null)
41+
{
42+
if (CheckPresence(type.BaseType, sourceMember, out var foundInBaseType))
43+
{
44+
return foundInBaseType;
45+
}
46+
47+
var result = FindTargetMember(type.BaseType, sourceMember, true);
48+
49+
if (result is not null)
50+
{
51+
return result;
52+
}
53+
}
54+
55+
foreach (var immediateInterface in type.GetImmediateInterfaces())
56+
{
57+
if (CheckPresence(immediateInterface, sourceMember, out var found))
58+
{
59+
return found;
60+
}
61+
62+
var result = FindTargetMember(immediateInterface, sourceMember, true);
63+
64+
if (result is not null)
65+
{
66+
return result;
67+
}
68+
}
69+
70+
return null;
71+
}
72+
73+
private static bool CheckPresence(Type type, MemberInfo sourceMember, out MemberInfo? found)
74+
{
75+
var memberInfos = type.GetMembers();
76+
77+
foreach (var member in memberInfos)
78+
{
79+
if (member.MemberType != sourceMember.MemberType)
80+
{
81+
continue;
82+
}
83+
84+
if (member.Name != sourceMember.Name)
85+
{
86+
continue;
87+
}
88+
89+
if (sourceMember is MethodInfo sourceMethod && member is MethodInfo targetMethod)
90+
{
91+
var sourceParams = sourceMethod.GetParameters();
92+
var targetParams = targetMethod.GetParameters();
93+
94+
if (sourceParams.Length != targetParams.Length)
95+
{
96+
continue;
97+
}
98+
99+
var parametersMatch = true;
100+
101+
for (var i = 0; i < sourceParams.Length; i++)
102+
{
103+
if (sourceParams[i].ParameterType != targetParams[i].ParameterType)
104+
{
105+
parametersMatch = false;
106+
break;
107+
}
108+
}
109+
110+
if (!parametersMatch)
111+
{
112+
continue;
113+
}
114+
}
115+
116+
found = member;
117+
118+
return true;
119+
}
120+
121+
found = null;
122+
123+
return false;
124+
}
125+
}

0 commit comments

Comments
 (0)