162 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C#
		
	
	
	
		
		
			
		
	
	
			162 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C#
		
	
	
	
| 
								 | 
							
								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 WebAPI.Areas.HelpPage.ModelDescriptions;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace 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;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |