You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

XmlDocumentationProvider.cs 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. using System;
  2. using System.Globalization;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Web.Http.Controllers;
  6. using System.Web.Http.Description;
  7. using System.Xml.XPath;
  8. using CacheControl_test.Areas.HelpPage.ModelDescriptions;
  9. namespace CacheControl_test.Areas.HelpPage
  10. {
  11. /// <summary>
  12. /// A custom <see cref="IDocumentationProvider"/> that reads the API documentation from an XML documentation file.
  13. /// </summary>
  14. public class XmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
  15. {
  16. private XPathNavigator _documentNavigator;
  17. private const string TypeExpression = "/doc/members/member[@name='T:{0}']";
  18. private const string MethodExpression = "/doc/members/member[@name='M:{0}']";
  19. private const string PropertyExpression = "/doc/members/member[@name='P:{0}']";
  20. private const string FieldExpression = "/doc/members/member[@name='F:{0}']";
  21. private const string ParameterExpression = "param[@name='{0}']";
  22. /// <summary>
  23. /// Initializes a new instance of the <see cref="XmlDocumentationProvider"/> class.
  24. /// </summary>
  25. /// <param name="documentPath">The physical path to XML document.</param>
  26. public XmlDocumentationProvider(string documentPath)
  27. {
  28. if (documentPath == null)
  29. {
  30. throw new ArgumentNullException("documentPath");
  31. }
  32. XPathDocument xpath = new XPathDocument(documentPath);
  33. _documentNavigator = xpath.CreateNavigator();
  34. }
  35. public string GetDocumentation(HttpControllerDescriptor controllerDescriptor)
  36. {
  37. XPathNavigator typeNode = GetTypeNode(controllerDescriptor.ControllerType);
  38. return GetTagValue(typeNode, "summary");
  39. }
  40. public virtual string GetDocumentation(HttpActionDescriptor actionDescriptor)
  41. {
  42. XPathNavigator methodNode = GetMethodNode(actionDescriptor);
  43. return GetTagValue(methodNode, "summary");
  44. }
  45. public virtual string GetDocumentation(HttpParameterDescriptor parameterDescriptor)
  46. {
  47. ReflectedHttpParameterDescriptor reflectedParameterDescriptor = parameterDescriptor as ReflectedHttpParameterDescriptor;
  48. if (reflectedParameterDescriptor != null)
  49. {
  50. XPathNavigator methodNode = GetMethodNode(reflectedParameterDescriptor.ActionDescriptor);
  51. if (methodNode != null)
  52. {
  53. string parameterName = reflectedParameterDescriptor.ParameterInfo.Name;
  54. XPathNavigator parameterNode = methodNode.SelectSingleNode(String.Format(CultureInfo.InvariantCulture, ParameterExpression, parameterName));
  55. if (parameterNode != null)
  56. {
  57. return parameterNode.Value.Trim();
  58. }
  59. }
  60. }
  61. return null;
  62. }
  63. public string GetResponseDocumentation(HttpActionDescriptor actionDescriptor)
  64. {
  65. XPathNavigator methodNode = GetMethodNode(actionDescriptor);
  66. return GetTagValue(methodNode, "returns");
  67. }
  68. public string GetDocumentation(MemberInfo member)
  69. {
  70. string memberName = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(member.DeclaringType), member.Name);
  71. string expression = member.MemberType == MemberTypes.Field ? FieldExpression : PropertyExpression;
  72. string selectExpression = String.Format(CultureInfo.InvariantCulture, expression, memberName);
  73. XPathNavigator propertyNode = _documentNavigator.SelectSingleNode(selectExpression);
  74. return GetTagValue(propertyNode, "summary");
  75. }
  76. public string GetDocumentation(Type type)
  77. {
  78. XPathNavigator typeNode = GetTypeNode(type);
  79. return GetTagValue(typeNode, "summary");
  80. }
  81. private XPathNavigator GetMethodNode(HttpActionDescriptor actionDescriptor)
  82. {
  83. ReflectedHttpActionDescriptor reflectedActionDescriptor = actionDescriptor as ReflectedHttpActionDescriptor;
  84. if (reflectedActionDescriptor != null)
  85. {
  86. string selectExpression = String.Format(CultureInfo.InvariantCulture, MethodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo));
  87. return _documentNavigator.SelectSingleNode(selectExpression);
  88. }
  89. return null;
  90. }
  91. private static string GetMemberName(MethodInfo method)
  92. {
  93. string name = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(method.DeclaringType), method.Name);
  94. ParameterInfo[] parameters = method.GetParameters();
  95. if (parameters.Length != 0)
  96. {
  97. string[] parameterTypeNames = parameters.Select(param => GetTypeName(param.ParameterType)).ToArray();
  98. name += String.Format(CultureInfo.InvariantCulture, "({0})", String.Join(",", parameterTypeNames));
  99. }
  100. return name;
  101. }
  102. private static string GetTagValue(XPathNavigator parentNode, string tagName)
  103. {
  104. if (parentNode != null)
  105. {
  106. XPathNavigator node = parentNode.SelectSingleNode(tagName);
  107. if (node != null)
  108. {
  109. return node.Value.Trim();
  110. }
  111. }
  112. return null;
  113. }
  114. private XPathNavigator GetTypeNode(Type type)
  115. {
  116. string controllerTypeName = GetTypeName(type);
  117. string selectExpression = String.Format(CultureInfo.InvariantCulture, TypeExpression, controllerTypeName);
  118. return _documentNavigator.SelectSingleNode(selectExpression);
  119. }
  120. private static string GetTypeName(Type type)
  121. {
  122. string name = type.FullName;
  123. if (type.IsGenericType)
  124. {
  125. // Format the generic type name to something like: Generic{System.Int32,System.String}
  126. Type genericType = type.GetGenericTypeDefinition();
  127. Type[] genericArguments = type.GetGenericArguments();
  128. string genericTypeName = genericType.FullName;
  129. // Trim the generic parameter counts from the name
  130. genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`'));
  131. string[] argumentTypeNames = genericArguments.Select(t => GetTypeName(t)).ToArray();
  132. name = String.Format(CultureInfo.InvariantCulture, "{0}{{{1}}}", genericTypeName, String.Join(",", argumentTypeNames));
  133. }
  134. if (type.IsNested)
  135. {
  136. // Changing the nested type name from OuterType+InnerType to OuterType.InnerType to match the XML documentation syntax.
  137. name = name.Replace("+", ".");
  138. }
  139. return name;
  140. }
  141. }
  142. }