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.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);
private static readonly Type _xPathExtensionsType = Type.GetType("System.Xml.XPath.Extensions, System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
public static string GetXmlDocumentation(this Type type)
{
return ((MemberInfo)type.GetTypeInfo()).GetXmlDocumentation();
}
public static string GetXmlDocumentation(this MemberInfo member)
{
if ((object)_xPathExtensionsType == null)
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));
}
}
public static string GetXmlDocumentation(this ParameterInfo parameter)
{
if ((object)_xPathExtensionsType == null)
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)
{
return ((MemberInfo)type.GetTypeInfo()).GetXmlDocumentation(pathToXmlFile);
}
public static string GetXmlDocumentation(this MemberInfo member, string pathToXmlFile)
{
try {
lock (_lock) {
if (pathToXmlFile != null && (object)_xPathExtensionsType != null) {
AssemblyName name = member.Module.Assembly.GetName();
if (_cache.ContainsKey(name.FullName) && _cache[name.FullName] == null)
return string.Empty;
if (DynamicFileExists(pathToXmlFile)) {
if (!_cache.ContainsKey(name.FullName))
_cache[name.FullName] = XDocument.Load(pathToXmlFile);
return member.GetXmlDocumentation(_cache[name.FullName]);
}
_cache[name.FullName] = null;
return string.Empty;
}
return string.Empty;
}
} catch {
return string.Empty;
}
}
public static string GetXmlDocumentation(this ParameterInfo parameter, string pathToXmlFile)
{
try {
lock (_lock) {
if (pathToXmlFile != null && (object)_xPathExtensionsType != null) {
AssemblyName name = parameter.Member.Module.Assembly.GetName();
if (_cache.ContainsKey(name.FullName) && _cache[name.FullName] == null)
return string.Empty;
if (DynamicFileExists(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 memberElementName = GetMemberElementName(member);
return DynamicXPathEvaluate(xml, $"""{new object[1] {
memberElementName
}}""").ToString().Trim();
}
private static string GetXmlDocumentation(this ParameterInfo parameter, XDocument xml)
{
string memberElementName = GetMemberElementName(parameter.Member);
if (parameter.IsRetval || string.IsNullOrEmpty(parameter.Name))
return DynamicXPathEvaluate(xml, $"""{new object[1] {
memberElementName
}}""").ToString().Trim();
return DynamicXPathEvaluate(xml, string.Format("string(/doc/members/member[@name='{0}']/param[@name='{1}'])", new object[2] {
memberElementName,
parameter.Name
})).ToString().Trim();
}
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 x.ParameterType.FullName).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}", new object[2] {
c,
text
});
}
private static string GetXmlDocumentationPath(dynamic assembly)
{
dynamic val = assembly.GetName();
dynamic val2 = XmlDocumentationExtensions.DynamicPathCombine(XmlDocumentationExtensions.DynamicPathGetDirectoryName(assembly.Location), val.Name + ".xml");
if (XmlDocumentationExtensions.DynamicFileExists(val2))
return (string)val2;
dynamic value = Type.GetType("System.AppDomain").GetRuntimeProperty("CurrentDomain").GetValue(null);
return (string)XmlDocumentationExtensions.DynamicPathCombine(value.BaseDirectory, val.Name + ".xml");
}
private static bool DynamicFileExists(string filePath)
{
return (bool)Type.GetType("System.IO.File", true).GetRuntimeMethod("Exists", new Type[1] {
typeof(string)
}).Invoke(null, new object[1] {
filePath
});
}
private static string DynamicPathCombine(string path1, string path2)
{
return (string)Type.GetType("System.IO.Path", true).GetRuntimeMethod("Combine", new Type[2] {
typeof(string),
typeof(string)
}).Invoke(null, new object[2] {
path1,
path2
});
}
private static string DynamicPathGetDirectoryName(string filePath)
{
return (string)Type.GetType("System.IO.Path", true).GetRuntimeMethod("GetDirectoryName", new Type[1] {
typeof(string)
}).Invoke(null, new object[1] {
filePath
});
}
private static object DynamicXPathEvaluate(XDocument document, string path)
{
return (string)_xPathExtensionsType.GetRuntimeMethod("XPathEvaluate", new Type[2] {
typeof(XDocument),
typeof(string)
}).Invoke(null, new object[2] {
document,
path
});
}
}
}