//Except where stated all code and programs in this project are the copyright of Jim Blackler, 2008. //jimblackler@gmail.com // //This is free software. Libraries and programs are distributed under the terms of the GNU Lesser //General Public License. Please see the files COPYING and COPYING.LESSER. using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Xml; namespace JimBlackler.DocsByReflection { /// /// Utility class to provide documentation for various types where available with the assembly /// public class DocsByReflection { /// /// Provides the documentation comments for a specific method /// /// The MethodInfo (reflection data ) of the member to find documentation for /// The XML fragment describing the method public static XmlElement XMLFromMember(MethodInfo methodInfo) { // Calculate the parameter string as this is in the member name in the XML string parametersString = ""; foreach (ParameterInfo parameterInfo in methodInfo.GetParameters()) { if (parametersString.Length > 0) { parametersString += ","; } parametersString += parameterInfo.ParameterType.FullName; } //AL: 15.04.2008 ==> BUG-FIX remove “()” if parametersString is empty if (parametersString.Length > 0) return XMLFromName(methodInfo.DeclaringType, 'M', methodInfo.Name + "(" + parametersString + ")"); else return XMLFromName(methodInfo.DeclaringType, 'M', methodInfo.Name); } /// /// Provides the documentation comments for a specific member /// /// The MemberInfo (reflection data) or the member to find documentation for /// The XML fragment describing the member public static XmlElement XMLFromMember(MemberInfo memberInfo) { // First character [0] of member type is prefix character in the name in the XML return XMLFromName(memberInfo.DeclaringType, memberInfo.MemberType.ToString()[0], memberInfo.Name); } /// /// Provides the documentation comments for a specific type /// /// Type to find the documentation for /// The XML fragment that describes the type public static XmlElement XMLFromType(Type type) { // Prefix in type names is T return XMLFromName(type, 'T', ""); } /// /// Obtains the XML Element that describes a reflection element by searching the /// members for a member that has a name that describes the element. /// /// The type or parent type, used to fetch the assembly /// The prefix as seen in the name attribute in the documentation XML /// Where relevant, the full name qualifier for the element /// The member that has a name that describes the specified reflection element private static XmlElement XMLFromName(Type type, char prefix, string name) { string fullName; if (String.IsNullOrEmpty(name)) { fullName = prefix + ":" + type.FullName; } else { fullName = prefix + ":" + type.FullName + "." + name; } XmlDocument xmlDocument = XMLFromAssembly(type.Assembly); XmlElement matchedElement = null; foreach (XmlElement xmlElement in xmlDocument["doc"]["members"]) { if (xmlElement.Attributes["name"].Value.Equals(fullName)) { if (matchedElement != null) { throw new DocsByReflectionException("Multiple matches to query", null); } matchedElement = xmlElement; } } if (matchedElement == null) { throw new DocsByReflectionException("Could not find documentation for specified element", null); } return matchedElement; } /// /// A cache used to remember Xml documentation for assemblies /// static Dictionary cache = new Dictionary(); /// /// A cache used to store failure exceptions for assembly lookups /// static Dictionary failCache = new Dictionary(); /// /// Obtains the documentation file for the specified assembly /// /// The assembly to find the XML document for /// The XML document /// This version uses a cache to preserve the assemblies, so that /// the XML file is not loaded and parsed on every single lookup public static XmlDocument XMLFromAssembly(Assembly assembly) { if (failCache.ContainsKey(assembly)) { throw failCache[assembly]; } try { if (!cache.ContainsKey(assembly)) { // load the docuemnt into the cache cache[assembly] = XMLFromAssemblyNonCached(assembly); } return cache[assembly]; } catch (Exception exception) { failCache[assembly] = exception; throw exception; } } /// /// Loads and parses the documentation file for the specified assembly /// /// The assembly to find the XML document for /// The XML document private static XmlDocument XMLFromAssemblyNonCached(Assembly assembly) { string assemblyFilename = assembly.CodeBase; const string prefix = "file:///"; if (assemblyFilename.StartsWith(prefix)) { StreamReader streamReader; try { streamReader = new StreamReader(Path.ChangeExtension(assemblyFilename.Substring(prefix.Length), ".xml")); } catch (FileNotFoundException exception) { throw new DocsByReflectionException("XML documentation not present (make sure it is turned on in project properties when building)", exception); } XmlDocument xmlDocument = new XmlDocument(); xmlDocument.Load(streamReader); return xmlDocument; } else { throw new DocsByReflectionException("Could not ascertain assembly filename", null); } } } }