Imports System Imports System.Globalization Imports System.Linq Imports System.Reflection Imports System.Web.Http.Controllers Imports System.Web.Http.Description Imports System.Xml.XPath Imports $rootnamespace$.Areas.HelpPage.ModelDescriptions Namespace Areas.HelpPage ''' ''' A custom that reads the API documentation from an XML documentation file. ''' Public Class XmlDocumentationProvider Implements IDocumentationProvider Implements IModelDocumentationProvider Private _documentNavigator As XPathNavigator Private Const TypeExpression As String = "/doc/members/member[@name='T:{0}']" Private Const MethodExpression As String = "/doc/members/member[@name='M:{0}']" Private Const PropertyExpression As String = "/doc/members/member[@name='P:{0}']" Private Const FieldExpression As String = "/doc/members/member[@name='F:{0}']" Private Const ParameterExpression As String = "param[@name='{0}']" ''' ''' Initializes a new instance of the class. ''' ''' The physical path to XML document. Public Sub New(documentPath As String) If (documentPath Is Nothing) Then Throw New ArgumentNullException("documentPath") End If Dim xpath As New XPathDocument(documentPath) _documentNavigator = xpath.CreateNavigator() End Sub Public Function GetDocumentation(controllerDescriptor As HttpControllerDescriptor) As String Implements IDocumentationProvider.GetDocumentation Dim typeNode As XPathNavigator = GetTypeNode(controllerDescriptor.ControllerType) Return GetTagValue(typeNode, "summary") End Function Public Function GetDocumentation(actionDescriptor As HttpActionDescriptor) As String Implements IDocumentationProvider.GetDocumentation Dim methodNode As XPathNavigator = GetMethodNode(actionDescriptor) Return GetTagValue(methodNode, "summary") End Function Public Function GetDocumentation(parameterDescriptor As HttpParameterDescriptor) As String Implements IDocumentationProvider.GetDocumentation Dim reflectedParameterDescriptor As ReflectedHttpParameterDescriptor = TryCast(parameterDescriptor, ReflectedHttpParameterDescriptor) If (Not reflectedParameterDescriptor Is Nothing) Then Dim methodNode As XPathNavigator = GetMethodNode(reflectedParameterDescriptor.ActionDescriptor) If (Not methodNode Is Nothing) Then Dim parameterName As String = reflectedParameterDescriptor.ParameterInfo.Name Dim parameterNode As XPathNavigator = methodNode.SelectSingleNode(String.Format(CultureInfo.InvariantCulture, ParameterExpression, parameterName)) If (Not parameterNode Is Nothing) Then Return parameterNode.Value.Trim() End If End If End If Return Nothing End Function Public Function GetResponseDocumentation(actionDescriptor As HttpActionDescriptor) As String Implements IDocumentationProvider.GetResponseDocumentation Dim methodNode As XPathNavigator = GetMethodNode(actionDescriptor) Return GetTagValue(methodNode, "returns") End Function Public Function GetDocumentation(member As MemberInfo) As String Implements IModelDocumentationProvider.GetDocumentation Dim memberName As String = [String].Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(member.DeclaringType), member.Name) Dim expression As String = If(member.MemberType = MemberTypes.Field, FieldExpression, PropertyExpression) Dim selectExpression As String = [String].Format(CultureInfo.InvariantCulture, expression, memberName) Dim propertyNode As XPathNavigator = _documentNavigator.SelectSingleNode(selectExpression) Return GetTagValue(propertyNode, "summary") End Function Public Function GetDocumentation(type As Type) As String Implements IModelDocumentationProvider.GetDocumentation Dim typeNode As XPathNavigator = GetTypeNode(type) Return GetTagValue(typeNode, "summary") End Function Private Function GetMethodNode(actionDescriptor As HttpActionDescriptor) As XPathNavigator Dim reflectedActionDescriptor As ReflectedHttpActionDescriptor = TryCast(actionDescriptor, ReflectedHttpActionDescriptor) If (Not reflectedActionDescriptor Is Nothing) Then Dim selectExpression As String = String.Format(CultureInfo.InvariantCulture, MethodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo)) Return _documentNavigator.SelectSingleNode(selectExpression) End If Return Nothing End Function Private Shared Function GetMemberName(method As MethodInfo) As String Dim name As String = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", method.DeclaringType.FullName, method.Name) Dim parameters() As ParameterInfo = method.GetParameters() If (parameters.Length <> 0) Then Dim parameterTypeNames() As String = parameters.Select(Function(param) GetTypeName(param.ParameterType)).ToArray() name += String.Format(CultureInfo.InvariantCulture, "({0})", String.Join(",", parameterTypeNames)) End If Return name End Function Private Shared Function GetTagValue(parentNode As XPathNavigator, tagName As String) As String If (Not parentNode Is Nothing) Then Dim node As XPathNavigator = parentNode.SelectSingleNode(tagName) If (Not node Is Nothing) Then Return node.Value.Trim() End If End If Return Nothing End Function Private Function GetTypeNode(type As Type) As XPathNavigator Dim controllerTypeName As String = GetTypeName(type) Dim selectExpression As String = [String].Format(CultureInfo.InvariantCulture, TypeExpression, controllerTypeName) Return _documentNavigator.SelectSingleNode(selectExpression) End Function Private Shared Function GetTypeName(type As Type) As String Dim name As String = type.FullName If type.IsGenericType Then ' Format the generic type name to something like: Generic{System.Int32,System.String} Dim genericType As Type = type.GetGenericTypeDefinition() Dim genericArguments As Type() = type.GetGenericArguments() Dim genericTypeName As String = genericType.FullName ' Trim the generic parameter counts from the name genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf("`"c)) Dim argumentTypeNames As String() = genericArguments.[Select](Function(t) GetTypeName(t)).ToArray() name = [String].Format(CultureInfo.InvariantCulture, "{0}{{{1}}}", genericTypeName, [String].Join(",", argumentTypeNames)) End If If type.IsNested Then ' Changing the nested type name from OuterType+InnerType to OuterType.InnerType to match the XML documentation syntax. name = name.Replace("+", ".") End If Return name End Function End Class End Namespace