对比新文件 |
| | |
| | | using System; |
| | | using System.Globalization; |
| | | using System.Linq; |
| | | using System.Reflection; |
| | | using System.Web.Http.Controllers; |
| | | using System.Web.Http.Description; |
| | | using System.Xml.XPath; |
| | | using IStation.WebApi.Areas.HelpPage.ModelDescriptions; |
| | | |
| | | namespace IStation.WebApi.Areas.HelpPage |
| | | { |
| | | /// <summary> |
| | | /// A custom <see cref="IDocumentationProvider"/> that reads the API documentation from an XML documentation file. |
| | | /// </summary> |
| | | public class XmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider |
| | | { |
| | | private XPathNavigator _documentNavigator; |
| | | private const string TypeExpression = "/doc/members/member[@name='T:{0}']"; |
| | | private const string MethodExpression = "/doc/members/member[@name='M:{0}']"; |
| | | private const string PropertyExpression = "/doc/members/member[@name='P:{0}']"; |
| | | private const string FieldExpression = "/doc/members/member[@name='F:{0}']"; |
| | | private const string ParameterExpression = "param[@name='{0}']"; |
| | | |
| | | /// <summary> |
| | | /// Initializes a new instance of the <see cref="XmlDocumentationProvider"/> class. |
| | | /// </summary> |
| | | /// <param name="documentPath">The physical path to XML document.</param> |
| | | public XmlDocumentationProvider(string documentPath) |
| | | { |
| | | if (documentPath == null) |
| | | { |
| | | throw new ArgumentNullException("documentPath"); |
| | | } |
| | | XPathDocument xpath = new XPathDocument(documentPath); |
| | | _documentNavigator = xpath.CreateNavigator(); |
| | | } |
| | | |
| | | public string GetDocumentation(HttpControllerDescriptor controllerDescriptor) |
| | | { |
| | | XPathNavigator typeNode = GetTypeNode(controllerDescriptor.ControllerType); |
| | | return GetTagValue(typeNode, "summary"); |
| | | } |
| | | |
| | | public virtual string GetDocumentation(HttpActionDescriptor actionDescriptor) |
| | | { |
| | | XPathNavigator methodNode = GetMethodNode(actionDescriptor); |
| | | return GetTagValue(methodNode, "summary"); |
| | | } |
| | | |
| | | public virtual string GetDocumentation(HttpParameterDescriptor parameterDescriptor) |
| | | { |
| | | ReflectedHttpParameterDescriptor reflectedParameterDescriptor = parameterDescriptor as ReflectedHttpParameterDescriptor; |
| | | if (reflectedParameterDescriptor != null) |
| | | { |
| | | XPathNavigator methodNode = GetMethodNode(reflectedParameterDescriptor.ActionDescriptor); |
| | | if (methodNode != null) |
| | | { |
| | | string parameterName = reflectedParameterDescriptor.ParameterInfo.Name; |
| | | XPathNavigator parameterNode = methodNode.SelectSingleNode(String.Format(CultureInfo.InvariantCulture, ParameterExpression, parameterName)); |
| | | if (parameterNode != null) |
| | | { |
| | | return parameterNode.Value.Trim(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | public string GetResponseDocumentation(HttpActionDescriptor actionDescriptor) |
| | | { |
| | | XPathNavigator methodNode = GetMethodNode(actionDescriptor); |
| | | return GetTagValue(methodNode, "returns"); |
| | | } |
| | | |
| | | public string GetDocumentation(MemberInfo member) |
| | | { |
| | | string memberName = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(member.DeclaringType), member.Name); |
| | | string expression = member.MemberType == MemberTypes.Field ? FieldExpression : PropertyExpression; |
| | | string selectExpression = String.Format(CultureInfo.InvariantCulture, expression, memberName); |
| | | XPathNavigator propertyNode = _documentNavigator.SelectSingleNode(selectExpression); |
| | | return GetTagValue(propertyNode, "summary"); |
| | | } |
| | | |
| | | public string GetDocumentation(Type type) |
| | | { |
| | | XPathNavigator typeNode = GetTypeNode(type); |
| | | return GetTagValue(typeNode, "summary"); |
| | | } |
| | | |
| | | private XPathNavigator GetMethodNode(HttpActionDescriptor actionDescriptor) |
| | | { |
| | | ReflectedHttpActionDescriptor reflectedActionDescriptor = actionDescriptor as ReflectedHttpActionDescriptor; |
| | | if (reflectedActionDescriptor != null) |
| | | { |
| | | string selectExpression = String.Format(CultureInfo.InvariantCulture, MethodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo)); |
| | | return _documentNavigator.SelectSingleNode(selectExpression); |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | private static string GetMemberName(MethodInfo method) |
| | | { |
| | | string name = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(method.DeclaringType), method.Name); |
| | | ParameterInfo[] parameters = method.GetParameters(); |
| | | if (parameters.Length != 0) |
| | | { |
| | | string[] parameterTypeNames = parameters.Select(param => GetTypeName(param.ParameterType)).ToArray(); |
| | | name += String.Format(CultureInfo.InvariantCulture, "({0})", String.Join(",", parameterTypeNames)); |
| | | } |
| | | |
| | | return name; |
| | | } |
| | | |
| | | private static string GetTagValue(XPathNavigator parentNode, string tagName) |
| | | { |
| | | if (parentNode != null) |
| | | { |
| | | XPathNavigator node = parentNode.SelectSingleNode(tagName); |
| | | if (node != null) |
| | | { |
| | | return node.Value.Trim(); |
| | | } |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | private XPathNavigator GetTypeNode(Type type) |
| | | { |
| | | string controllerTypeName = GetTypeName(type); |
| | | string selectExpression = String.Format(CultureInfo.InvariantCulture, TypeExpression, controllerTypeName); |
| | | return _documentNavigator.SelectSingleNode(selectExpression); |
| | | } |
| | | |
| | | private static string GetTypeName(Type type) |
| | | { |
| | | string name = type.FullName; |
| | | if (type.IsGenericType) |
| | | { |
| | | // Format the generic type name to something like: Generic{System.Int32,System.String} |
| | | Type genericType = type.GetGenericTypeDefinition(); |
| | | Type[] genericArguments = type.GetGenericArguments(); |
| | | string genericTypeName = genericType.FullName; |
| | | |
| | | // Trim the generic parameter counts from the name |
| | | genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`')); |
| | | string[] argumentTypeNames = genericArguments.Select(t => GetTypeName(t)).ToArray(); |
| | | name = String.Format(CultureInfo.InvariantCulture, "{0}{{{1}}}", genericTypeName, String.Join(",", argumentTypeNames)); |
| | | } |
| | | if (type.IsNested) |
| | | { |
| | | // Changing the nested type name from OuterType+InnerType to OuterType.InnerType to match the XML documentation syntax. |
| | | name = name.Replace("+", "."); |
| | | } |
| | | |
| | | return name; |
| | | } |
| | | } |
| | | } |