//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);
}
}
}
}