using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Xml;
using NDoc3.Core;
using NDoc3.Xml;
namespace NDoc3.Documenter.Msdn {
///
///
public class NameResolver {
public const string EXT = ".html";
public MsdnXsltUtilities utilities;
private readonly bool mergeAssemblies;
private readonly StringDictionary fileNames = new StringDictionary();
private readonly StringDictionary elemNames = new StringDictionary();
private readonly ReferenceTypeDictionary assemblyReferences = new ReferenceTypeDictionary();
///
///
///
///
public NameResolver(XmlDocument documentation, bool mergeAssemblies) {
this.mergeAssemblies = mergeAssemblies;
BuildNameTables(documentation, mergeAssemblies);
}
#region Used for Html file generation
///
///
///
///
///
public string GetFilenameForFieldList(string assemblyName, string typeID) {
return GetFilenameForIdSpecial(assemblyName, typeID, "~Fields");
}
///
///
///
///
///
public string GetFilenameForOperatorList(string assemblyName, string typeID) {
return GetFilenameForIdSpecial(assemblyName, typeID, "~Operators");
}
///
///
///
///
///
public string GetFilenameForMethodList(string assemblyName, string typeID) {
return GetFilenameForIdSpecial(assemblyName, typeID, "~Methods");
}
///
///
///
///
///
public string GetFilenameForPropertyList(string assemblyName, string typeID) {
return GetFilenameForIdSpecial(assemblyName, typeID, "~Properties");
}
///
///
///
///
///
public string GetFilenameForEventList(string assemblyName, string typeID) {
return GetFilenameForIdSpecial(assemblyName, typeID, "~Events");
}
#endregion
// exposed to XSLT
///
///
///
///
///
public string GetDisplayNameForId(string currentAssemblyName, string memberId) {
string name = elemNames[currentAssemblyName + memberId];
if (name == null) {
// search for identifier in referenced assemblies
string[] assemblyReference = assemblyReferences[currentAssemblyName];
if (assemblyReference != null) {
foreach (string assemblyName in assemblyReference) {
name = elemNames[assemblyName + memberId];
if (name != null)
break;
}
}
}
if (name == null) {
name = elemNames[memberId];
}
return name;
}
// exposed to XSLT
///
///
///
///
///
public string GetFilenameForId(string currentAssemblyName, string memberId) {
// lookup current assembly
string filename = GetFilenameForIdInternal(currentAssemblyName, memberId);
return filename;
}
///
///
///
///
public string GetFilenameForAssembly(string assemblyName) {
return GetFilenameForId(assemblyName, null);
}
// exposed to XSLT
///
///
///
///
///
public string GetFilenameForNamespaceHierarchy(string assemblyName, string namespaceName) {
return GetFilenameForIdSpecial(assemblyName, "N:" + namespaceName, "~Hierarchy");
}
// exposed to XSLT
///
///
///
///
///
public string GetFilenameForNamespace(string assemblyName, string namespaceName) {
if (mergeAssemblies)
assemblyName = string.Empty;
if (string.IsNullOrEmpty(namespaceName))
namespaceName = "(global)";
return GetFilenameForId(assemblyName, "N:" + namespaceName);
}
// exposed to XSLT
///
///
///
///
///
public string GetFilenameForTypeHierarchy(string assemblyName, string typeID) {
return GetFilenameForIdSpecial(assemblyName, typeID, "~Hierarchy");
}
// exposed to XSLT
///
///
///
///
///
public string GetFilenameForTypeMemberList(string assemblyName, string typeID) {
return GetFilenameForIdSpecial(assemblyName, typeID, "~Members");
}
// exposed to XSLT
///
///
///
///
///
public string GetFilenameForConstructorList(string assemblyName, string typeID) {
return GetFilenameForIdSpecial(assemblyName, typeID, "~Constructors");
}
// exposed to XSLT
///
///
///
///
///
///
public string GetFilenameForOperatorOverloads(string assemblyName, string typeID, string operatorName) {
return GetFilenameForIdSpecial(assemblyName, typeID, "." + operatorName.Replace('`', '$') + "~Overloads");
}
// exposed to XSLT
///
///
///
///
///
///
public string GetFilenameForPropertyOverloads(string assemblyName, string typeID, string propertyName) {
string fileName = GetFilenameForIdSpecial(assemblyName, typeID, "." + propertyName.Replace('`', '$') + "~Overloads");
return fileName;
}
// exposed to XSLT
///
///
///
///
///
///
public string GetFilenameForMethodOverloads(string assemblyName, string typeID, string methodName) {
string fileName = GetFilenameForIdSpecial(assemblyName, typeID, "." + methodName.Replace('`', '$').Replace("<", "(").Replace(">", ")") + "~Overloads");
return fileName;
}
// exposed to XSLT
///
///
///
///
///
public string GetFilenameForTypename(string currentAssemblyName, string typeName) {
// strip leading type identifier
if (typeName.Length > 1 && typeName[1] != ':')
typeName = "T:" + typeName;
string filename = GetFilenameForId(currentAssemblyName, typeName);
return filename;
}
// exposed
///
///
///
///
///
///
public string GetFilenameForCRefOverload(string currentAssemblyName, string cref, string overload) {
// lookup current assembly
string filename = GetFilenameForId(currentAssemblyName, cref);
return filename;
#region Original XSLT Logic
/*
*/
#endregion
}
#region BuildNameTables
private void BuildNameTables(XmlDocument xmlDocumentation, bool mergeNamespaces) {
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDocumentation.NameTable);
nsmgr.AddNamespace("ns", "urn:ndoc-schema");
XmlNodeList assemblies = xmlDocumentation.SelectNodes("/ns:ndoc/ns:assembly", nsmgr);
if(assemblies == null) throw new Exception("No assemblies found");
foreach (XmlElement assemblyNode in assemblies) {
string assemblyName = GetNodeName(assemblyNode);
RegisterAssembly(assemblyName);
// build list of assemblyReferences
XmlNodeList assemblyReferenceNodes = assemblyNode.SelectNodes("ns:assemblyReference", nsmgr);
if(assemblyReferenceNodes == null) throw new Exception("No assembly reference nodes found");
List assemblyReferenceNames = new List();
foreach (XmlNode assemblyReferenceNode in assemblyReferenceNodes) {
assemblyReferenceNames.Add(GetNodeName(assemblyReferenceNode));
}
assemblyReferences.Add(assemblyName, assemblyReferenceNames.ToArray());
// foreach namespace
XmlNodeList namespaces = assemblyNode.SelectNodes("ns:module/ns:namespace", nsmgr);
if(namespaces == null) throw new Exception("No namespaces found");
foreach (XmlElement namespaceNode in namespaces) {
string namespaceName = GetNodeName(namespaceNode);
// register namespace
RegisterNamespace((mergeNamespaces ? string.Empty : assemblyName), namespaceName);
XmlNodeList types = namespaceNode.SelectNodes("*[@id]", nsmgr);
if(types == null) throw new Exception("No types found");
foreach (XmlElement typeNode in types) {
string typeId = GetNodeId(typeNode);
//TODO The rest should also use displayName ((EE): clarify what above line means - shall we remove 'name' attribute then?)
string typeDisplayName = GetNodeDisplayName(typeNode);
RegisterType(assemblyName, typeId, typeDisplayName);
// foreach member
XmlNodeList members = typeNode.SelectNodes("*[@id]");
if(members == null) throw new Exception("No type members found");
foreach (XmlElement memberNode in members) {
string memberId = GetNodeId(memberNode);
switch (memberNode.Name) {
case "constructor": {
string overload = XmlUtils.GetAttributeString(memberNode, "overload", false);
RegisterConstructor(assemblyName, typeId, memberId, overload);
}
break;
case "field": {
bool isEnum = (typeNode.Name == "enumeration");
string memberName = GetNodeName(memberNode);
RegisterField(assemblyName, typeId, memberId, isEnum, memberName);
}
break;
case "property": {
string overload = GetNodeOverload(memberNode);
string memberName = GetNodeName(memberNode);
RegisterProperty(assemblyName, memberId, memberName, overload);
}
break;
case "method": {
string overload = GetNodeOverload(memberNode);
string memberDisplayName = GetNodeDisplayName(memberNode);
RegisterMethod(assemblyName, memberId, memberDisplayName, overload);
}
break;
case "operator": {
string overload = GetNodeOverload(memberNode);
string memberName = GetNodeName(memberNode);
RegisterOperator(assemblyName, memberId, memberName, overload);
}
break;
case "event": {
string memberName = GetNodeName(memberNode);
RegisterEvent(assemblyName, memberId, memberName);
}
break;
}
}
}
}
}
}
private void RegisterAssembly(string assemblyName) {
Register(assemblyName, null, assemblyName, CalculateFilenameForId(assemblyName, null, null));
}
private void RegisterNamespace(string assemblyName, string namespaceName) {
if (string.IsNullOrEmpty(namespaceName)) {
namespaceName = "(global)";
}
string namespaceId = "N:" + namespaceName;
Register(assemblyName, namespaceId, namespaceName, CalculateFilenameForId(assemblyName, namespaceId, null));
}
private void RegisterType(string assemblyName, string typeId, string displayName) {
Register(assemblyName, typeId, displayName, CalculateFilenameForId(assemblyName, typeId, null));
}
private void RegisterConstructor(string assemblyName, string typeId, string id, string overload) {
Register(assemblyName, id, GetDisplayNameForId(assemblyName, typeId), CalculateFilenameForId(assemblyName, id, overload));
}
private void RegisterOperator(string assemblyName, string memberId, string memberName, string overload) {
Register(assemblyName, memberId, memberName, CalculateFilenameForId(assemblyName, memberId, overload));
}
private void RegisterMethod(string assemblyName, string memberId, string memberDisplayName, string overload) {
Register(assemblyName, memberId, memberDisplayName, CalculateFilenameForId(assemblyName, memberId, overload));
}
private void RegisterProperty(string assemblyName, string memberId, string memberName, string overload) {
Register(assemblyName, memberId, memberName, CalculateFilenameForId(assemblyName, memberId, overload));
}
private void RegisterField(string assemblyName, string typeId, string memberId, bool isEnum, string memberName) {
if (isEnum) {
Register(assemblyName, memberId, memberName, GetFilenameForId(assemblyName, typeId));
} else {
Register(assemblyName, memberId, memberName, CalculateFilenameForId(assemblyName, memberId, null));
}
}
private void RegisterEvent(string assemblyName, string memberId, string memberName) {
Register(assemblyName, memberId, memberName, CalculateFilenameForId(assemblyName, memberId, null));
}
#endregion
#region Registration & Lookup Logic
private string GetFilenameForIdInternal(string currentAssemblyName, string memberId) {
string filename = fileNames[currentAssemblyName + memberId];
if (filename == null) {
// search for identifier in referenced assemblies
string[] assemblyReference = assemblyReferences[currentAssemblyName];
if (assemblyReference != null) {
foreach (string assemblyName in assemblyReference) {
filename = fileNames[assemblyName + memberId];
if (filename != null)
break;
}
}
}
if (filename == null && memberId != null) {
filename = fileNames[memberId];
}
// Debug.Assert(filename != null, string.Format("Filename for assembly:memberId [{0}:{1}] not found", currentAssemblyName, memberId));
// Debug.WriteLine(string.Format("GetFilenameForIdInternal('{0}','{1}') => {2}", currentAssemblyName, memberId, filename));
if (filename != null)
return filename;
if (utilities != null)
{
if (memberId.Length > 2 && memberId[1] == ':')
memberId = memberId.Substring(2);
filename = utilities.FormatOnlineSDKLink(memberId);
if (filename != null)
return filename;
}
return String.Empty;
}
private string GetFilenameForIdSpecial(string assemblyName, string memberId, string postfix) {
string fn = GetFilenameForIdInternal(assemblyName, memberId);
if (fn != null && fn.Length > EXT.Length) {
fn = fn.Insert(fn.Length - EXT.Length, postfix);
}
// Debug.WriteLine(string.Format("GetFilenameForIdSpecial('{0}','{1}') => {2}", assemblyName, memberId, fn));
return fn;
}
private void Register(string assemblyName, string id, string displayName, string fileName) {
// Debug.WriteLine(string.Format("Registering [{0},{1}]=[{2},{3}]", assemblyName, id, displayName, fileName));
fileNames[assemblyName + id] = fileName;
elemNames[assemblyName + id] = displayName;
}
///
/// of the form "T:XXX", "F:XXX" etc
///
private static string CalculateFilenameForId(string assemblyName, string id, string overload) {
if (id == null) {
return assemblyName + EXT;
}
// char idType = '\0';
int ix = id.IndexOf(':');
// if (ix > -1) {
// idType = id[0];
// }
id = id.Substring(ix + 1);
// constructors could be #ctor or #cctor
// int ixDotHash = id.IndexOf(".#c");
// if (ixDotHash > -1)
// id = id.Substring(0, ixDotHash);
// methods could have "("
int ixLBrace = id.IndexOf("(");
if (ixLBrace > -1)
id = id.Substring(0, ixLBrace);
if (overload != null) {
id += overload;
}
id = id.Replace('#', '~');
// generic methods have the form "methodname"
id = id.Replace('<', '{');
id = id.Replace('>', '}');
id = id.Replace('`', '$');
return assemblyName + "~" + id + EXT;
}
#endregion
#region Xml Utility Methods
private static string GetNodeOverload(XmlNode memberNode) {
return XmlUtils.GetAttributeString(memberNode, "overload", false);
}
private static string GetNodeId(XmlNode node) {
return XmlUtils.GetNodeId(node);
}
private static string GetNodeName(XmlNode node) {
return XmlUtils.GetNodeName(node);
}
private static string GetNodeDisplayName(XmlNode node) {
return XmlUtils.GetNodeDisplayName(node);
}
#endregion
}
}