XmlDocumentationExtensions
Provides extension methods for reading XML comments from reflected members.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml.Linq;
namespace NJsonSchema.Infrastructure
{
public static class XmlDocumentationExtensions
{
private static readonly object Lock = new object();
private static readonly Dictionary<string, XDocument> Cache = new Dictionary<string, XDocument>(StringComparer.OrdinalIgnoreCase);
public static string GetXmlSummary(this MemberInfo member)
{
return member.GetXmlDocumentation("summary");
}
public static string GetXmlRemarks(this MemberInfo member)
{
return member.GetXmlDocumentation("remarks");
}
[Obsolete("Use GetXmlSummary instead.")]
public static string GetXmlDocumentation(this MemberInfo member)
{
return member.GetXmlDocumentation("summary");
}
public static string GetXmlDocumentation(this MemberInfo member, string tagName)
{
if (!DynamicApis.SupportsXPathApis || !DynamicApis.SupportsFileApis)
return string.Empty;
lock (Lock) {
AssemblyName name = member.Module.Assembly.GetName();
if (Cache.ContainsKey(name.FullName) && Cache[name.FullName] == null)
return string.Empty;
return member.GetXmlDocumentation(GetXmlDocumentationPath(member.Module.Assembly), tagName);
}
}
public static string GetXmlDocumentation(this ParameterInfo parameter)
{
if (!DynamicApis.SupportsXPathApis || !DynamicApis.SupportsFileApis)
return string.Empty;
lock (Lock) {
AssemblyName name = parameter.Member.Module.Assembly.GetName();
if (Cache.ContainsKey(name.FullName) && Cache[name.FullName] == null)
return string.Empty;
return parameter.GetXmlDocumentation(GetXmlDocumentationPath(parameter.Member.Module.Assembly));
}
}
public static string GetXmlDocumentation(this Type type, string pathToXmlFile, string tagName)
{
return type.GetTypeInfo().GetXmlDocumentation(pathToXmlFile, tagName);
}
public static string GetXmlDocumentation(this MemberInfo member, string pathToXmlFile, string tagName)
{
try {
if (pathToXmlFile != null && DynamicApis.SupportsXPathApis && DynamicApis.SupportsFileApis) {
lock (Lock) {
AssemblyName name = member.Module.Assembly.GetName();
if (Cache.ContainsKey(name.FullName) && Cache[name.FullName] == null)
return string.Empty;
if (DynamicApis.FileExists(pathToXmlFile)) {
if (!Cache.ContainsKey(name.FullName))
Cache[name.FullName] = XDocument.Load(pathToXmlFile);
return member.GetXmlDocumentation(Cache[name.FullName], tagName);
}
Cache[name.FullName] = null;
return string.Empty;
}
}
return string.Empty;
} catch {
return string.Empty;
}
}
public static string GetXmlDocumentation(this ParameterInfo parameter, string pathToXmlFile)
{
try {
if (pathToXmlFile != null && DynamicApis.SupportsXPathApis && DynamicApis.SupportsFileApis) {
lock (Lock) {
AssemblyName name = parameter.Member.Module.Assembly.GetName();
if (Cache.ContainsKey(name.FullName) && Cache[name.FullName] == null)
return string.Empty;
if (DynamicApis.FileExists(pathToXmlFile)) {
if (!Cache.ContainsKey(name.FullName))
Cache[name.FullName] = XDocument.Load(pathToXmlFile);
return parameter.GetXmlDocumentation(Cache[name.FullName]);
}
Cache[name.FullName] = null;
return string.Empty;
}
}
return string.Empty;
} catch {
return string.Empty;
}
}
private static string GetXmlDocumentation(this MemberInfo member, XDocument xml, string tagName)
{
string memberElementName = GetMemberElementName(member);
return RemoveLineBreakWhiteSpaces(DynamicApis.XPathEvaluate(xml, string.Format("string(/doc/members/member[@name='{0}']/" + tagName + ")", memberElementName)).ToString().Trim());
}
private static string GetXmlDocumentation(this ParameterInfo parameter, XDocument xml)
{
string memberElementName = GetMemberElementName(parameter.Member);
string documentation = (!parameter.IsRetval && !string.IsNullOrEmpty(parameter.Name)) ? DynamicApis.XPathEvaluate(xml, $"""{memberElementName}""{parameter.Name}""").ToString().Trim() : DynamicApis.XPathEvaluate(xml, $"""{memberElementName}""").ToString().Trim();
return RemoveLineBreakWhiteSpaces(documentation);
}
private static string RemoveLineBreakWhiteSpaces(string documentation)
{
return Regex.Replace(documentation.Replace("\r", string.Empty), "\\n\\s+", "\n");
}
private static string GetMemberElementName(dynamic member)
{
string text = (string)((member is Type) ? ((Type)member).FullName : (member.DeclaringType.FullName + "." + member.Name));
char c;
switch ((string)member.MemberType.ToString()) {
case "Constructor":
text = text.Replace(".ctor", "#ctor");
goto case "Method";
case "Method": {
c = 'M';
string text2 = string.Join(",", (from x in ((MethodBase)member).GetParameters()
select Regex.Replace(x.ParameterType.FullName, "(`[0-9]+)|(, .*?PublicKeyToken=[0-9a-z]*)", string.Empty).Replace("[[", "{").Replace("]]", "}")).ToArray());
if (!string.IsNullOrEmpty(text2))
text = text + "(" + text2 + ")";
break;
}
case "Event":
c = 'E';
break;
case "Field":
c = 'F';
break;
case "NestedType":
text = text.Replace('+', '.');
goto case "TypeInfo";
case "TypeInfo":
c = 'T';
break;
case "Property":
c = 'P';
break;
default:
throw new ArgumentException("Unknown member type.", "member");
}
return string.Format("{0}:{1}", c, text.Replace("+", "."));
}
private static string GetXmlDocumentationPath(dynamic assembly)
{
try {
if (!((assembly == null) ? true : false)) {
if (!(string.IsNullOrEmpty(assembly.Location) ? true : false)) {
dynamic val = assembly.GetName();
if (!(string.IsNullOrEmpty(val.Name) ? true : false)) {
dynamic val2 = DynamicApis.PathCombine(DynamicApis.PathGetDirectoryName(assembly.Location), val.Name + ".xml");
if (!(DynamicApis.FileExists(val2) ? true : false)) {
if (((object)assembly).GetType().GetRuntimeProperty("CodeBase") != (PropertyInfo)null) {
val2 = DynamicApis.PathCombine(DynamicApis.PathGetDirectoryName(assembly.CodeBase.Replace("file:///", string.Empty)), val.Name + ".xml").Replace("file:\\", string.Empty);
if (DynamicApis.FileExists(val2))
return (string)val2;
}
dynamic value = ReflectionExtensions.GetValue(Type.GetType("System.AppDomain").GetRuntimeProperty("CurrentDomain"), null);
val2 = DynamicApis.PathCombine(value.BaseDirectory, val.Name + ".xml");
if (!(DynamicApis.FileExists(val2) ? true : false))
return (string)DynamicApis.PathCombine(value.BaseDirectory, "bin\\" + val.Name + ".xml");
return (string)val2;
}
return (string)val2;
}
return null;
}
return null;
}
return null;
} catch {
return null;
}
}
}
}