// MsdnDocumenter.cs - a MSDN-like documenter
// Copyright (C) 2001 Kral Ferch, Jason Diamond
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.Globalization;
using NDoc3.Core;
using NDoc3.Core.Reflection;
using NDoc3.Documenter.Msdn.onlinefiles;
using NDoc3.Documenter.Msdn.onlinetemplates;
using NDoc3.Xml;
namespace NDoc3.Documenter.Msdn
{
/// The MsdnDocumenter class.
public class MsdnDocumenter : BaseReflectionDocumenter
{
private enum WhichType
{
Class,
Interface,
Structure,
Enumeration,
Delegate,
Unknown
};
private readonly Dictionary lowerCaseTypeNames;
private readonly Dictionary mixedCaseTypeNames;
private List filesToInclude = new List();
///
/// Initializes a new instance of the
/// class.
///
public MsdnDocumenter(MsdnDocumenterConfig config)
: base(config)
{
lowerCaseTypeNames = new Dictionary();
lowerCaseTypeNames.Add(WhichType.Class, "class");
lowerCaseTypeNames.Add(WhichType.Interface, "interface");
lowerCaseTypeNames.Add(WhichType.Structure, "structure");
lowerCaseTypeNames.Add(WhichType.Enumeration, "enumeration");
lowerCaseTypeNames.Add(WhichType.Delegate, "delegate");
mixedCaseTypeNames = new Dictionary();
mixedCaseTypeNames.Add(WhichType.Class, "Class");
mixedCaseTypeNames.Add(WhichType.Interface, "Interface");
mixedCaseTypeNames.Add(WhichType.Structure, "Structure");
mixedCaseTypeNames.Add(WhichType.Enumeration, "Enumeration");
mixedCaseTypeNames.Add(WhichType.Delegate, "Delegate");
}
/// See .
public override string MainOutputFile
{
get
{
if ((MyConfig.OutputTarget & OutputType.HtmlHelp) > 0) {
return Path.Combine(MyConfig.OutputDirectory,
MyConfig.HtmlHelpName + ".chm");
}
return Path.Combine(MyConfig.OutputDirectory, "index.html");
}
}
/// See .
public override string CanBuild(Project project, bool checkInputOnly)
{
string result = base.CanBuild(project, checkInputOnly);
if (result != null) {
return result;
}
string AdditionalContentResourceDirectory = MyConfig.AdditionalContentResourceDirectory;
if (AdditionalContentResourceDirectory.Length != 0 && !Directory.Exists(AdditionalContentResourceDirectory))
return string.Format("The Additional Content Resource Directory {0} could not be found", AdditionalContentResourceDirectory);
string ExtensibilityStylesheet = MyConfig.ExtensibilityStylesheet;
if (ExtensibilityStylesheet.Length != 0 && !File.Exists(ExtensibilityStylesheet))
return string.Format("The Extensibility Stylesheet file {0} could not be found", ExtensibilityStylesheet);
if (checkInputOnly) {
return null;
}
string path = Path.Combine(MyConfig.OutputDirectory,
MyConfig.HtmlHelpName + ".chm");
string temp = Path.Combine(MyConfig.OutputDirectory, "~chm.tmp");
try {
if (File.Exists(path)) {
//if we can move the file, then it is not open...
File.Move(path, temp);
File.Move(temp, path);
}
} catch (Exception) {
result = "The compiled HTML Help file is probably open.\nPlease close it and try again.";
}
return result;
}
/// See .
public override void Build(Project project)
{
BuildProjectContext buildContext = new BuildProjectContext(new CultureInfo(MyConfig.LangID),
new DirectoryInfo(MyConfig.OutputDirectory), MyConfig.CleanIntermediates);
try {
OnDocBuildingStep(0, "Initializing...");
buildContext.Initialize();
OnDocBuildingStep(10, "Merging XML documentation...");
// Will hold the name of the file name containing the XML doc
XmlDocument projectXml = CreateNDocXml(project);
buildContext.SetProjectXml(projectXml, MyConfig.MergeAssemblies);
OnDocBuildingStep(30, "Loading XSLT files...");
buildContext.stylesheets = StyleSheetCollection.LoadStyleSheets(MyConfig.ExtensibilityStylesheet);
OnDocBuildingStep(40, "Generating HTML pages...");
// setup for root page
string defaultTopic;
string rootPageFileName = null;
string rootPageTOCName = null;
if (!String.IsNullOrEmpty(MyConfig.RootPageFileName)) {
rootPageFileName = MyConfig.RootPageFileName;
defaultTopic = "default.html";
rootPageTOCName = "Overview";
// what to call the top page in the table of contents?
if (!String.IsNullOrEmpty(MyConfig.RootPageTOCName)) {
rootPageTOCName = MyConfig.RootPageTOCName;
}
} else {
// TODO (EE): check MergeAssemblies and adjust defaultTopic accordingly
XmlNode defaultNamespace;
if (MyConfig.MergeAssemblies)
{
XmlNodeList namespaceNodes = buildContext.SelectNodes("/ndoc:ndoc/ndoc:assembly/ndoc:module/ndoc:namespace");
int[] indexes = SortNodesByAttribute(namespaceNodes, "name");
defaultNamespace = namespaceNodes[indexes[0]];
}
else
{
XmlNodeList assemblyNodes = buildContext.SelectNodes("/ndoc:ndoc/ndoc:assembly");
int[] assemblyIndexes = SortNodesByAttribute(assemblyNodes, "name");
XmlNode defaultAssemblyNode = assemblyNodes[assemblyIndexes[0]];
XmlNodeList namespaceNodes = buildContext.SelectNodes(defaultAssemblyNode, "ndoc:module/ndoc:namespace");
int[] indexes = SortNodesByAttribute(namespaceNodes, "name");
defaultNamespace = namespaceNodes[indexes[0]];
}
string defaultNamespaceName = GetNodeName(defaultNamespace);
string assemblyName = GetNodeName(buildContext.SelectSingleNode(defaultNamespace, "ancestor::ndoc:assembly"));
defaultTopic = buildContext._nameResolver.GetFilenameForNamespace(assemblyName, defaultNamespaceName);
}
buildContext.htmlHelp = SetupHtmlHelpBuilder(buildContext.WorkingDirectory, defaultTopic);
using (buildContext.htmlHelp.OpenProjectFile())
using (buildContext.htmlHelp.OpenContentsFile(string.Empty, true)) {
// Write the embedded css files to the html output directory
WriteHtmlContentResources(buildContext);
GenerateHtmlContentFiles(buildContext, rootPageFileName, rootPageTOCName);
}
HtmlHelp htmlHelp = buildContext.htmlHelp;
htmlHelp.WriteEmptyIndexFile();
if ((MyConfig.OutputTarget & OutputType.Web) > 0) {
OnDocBuildingStep(75, "Generating HTML index file...");
// Write the embedded online templates to the html output directory
GenerateHtmlIndexFile(buildContext, defaultTopic);
}
if ((MyConfig.OutputTarget & OutputType.HtmlHelp) > 0) {
OnDocBuildingStep(85, "Compiling HTML Help file...");
htmlHelp.CompileProject();
}
#if !DEBUG
else
{
//remove .hhc file
File.Delete(htmlHelp.GetPathToContentsFile());
}
#endif
// if we're only building a CHM, copy that to the Outpur dir
if ((MyConfig.OutputTarget & OutputType.HtmlHelp) > 0 && (MyConfig.OutputTarget & OutputType.Web) == 0) {
buildContext.SaveOutputs("*.chm");
} else {
// otherwise copy everything to the output dir (cause the help file is all the html, not just one chm)
buildContext.SaveOutputs("*.*");
}
OnDocBuildingStep(100, "Done.");
} catch(DocumenterException) {
throw;
} catch (Exception ex) {
throw new DocumenterException(ex.Message, ex);
} finally {
buildContext.Dispose();
}
}
private void GenerateHtmlIndexFile(BuildProjectContext ctx, string defaultTopic)
{
EmbeddedResources.WriteEmbeddedResources(typeof(OnlineFilesLocationHint), ctx.WorkingDirectory);
using (TemplateWriter indexWriter = new TemplateWriter(
Path.Combine(ctx.WorkingDirectory.FullName, "index.html"),
EmbeddedResources.GetEmbeddedResourceReader(typeof(OnlineTemplatesLocationHint), "index.html", null))) {
indexWriter.CopyToLine("\t\t<%TITLE%>");
indexWriter.WriteLine("\t\t" + MyConfig.HtmlHelpName + "");
indexWriter.CopyToLine("\t\t\" frameborder=\"1\">");
indexWriter.WriteLine("\t\t");
indexWriter.CopyToEnd();
indexWriter.Close();
}
Trace.WriteLine("transform the HHC contents file into html");
#if DEBUG
int start = Environment.TickCount;
#endif
//transform the HHC contents file into html
using (StreamReader contentsFile = new StreamReader(ctx.HtmlHelpContentFilePath.FullName, ctx.CurrentFileEncoding)) {
XPathDocument xpathDocument = new XPathDocument(contentsFile);
string contentsFilename = Path.Combine(ctx.WorkingDirectory.FullName, "contents.html");
using (StreamWriter streamWriter = new StreamWriter(
File.Open(contentsFilename, FileMode.CreateNew, FileAccess.Write, FileShare.None), ctx.CurrentFileEncoding)) {
XslTransform(ctx, "htmlcontents", xpathDocument, null, streamWriter, contentsFilename);
}
}
#if DEBUG
Trace.WriteLine(string.Format("{0} msec.", (Environment.TickCount - start)));
#endif
}
private HtmlHelp SetupHtmlHelpBuilder(DirectoryInfo workingDirectory, string defaultTopic)
{
HtmlHelp htmlHelp = new HtmlHelp(
workingDirectory,
MyConfig.HtmlHelpName,
defaultTopic,
((MyConfig.OutputTarget & OutputType.HtmlHelp) == 0));
htmlHelp.IncludeFavorites = MyConfig.IncludeFavorites;
htmlHelp.BinaryTOC = MyConfig.BinaryTOC;
htmlHelp.LangID = MyConfig.LangID;
return htmlHelp;
}
private void GenerateHtmlContentFiles(BuildProjectContext buildContext, string rootPageFileName, string rootPageTOCName)
{
if (!String.IsNullOrEmpty(MyConfig.CopyrightHref)) {
if (!MyConfig.CopyrightHref.StartsWith("http:")) {
string copyrightFile = Path.Combine(buildContext.WorkingDirectory.FullName, Path.GetFileName(MyConfig.CopyrightHref));
File.Copy(MyConfig.CopyrightHref, copyrightFile, true);
File.SetAttributes(copyrightFile, FileAttributes.Archive);
buildContext.htmlHelp.AddFileToProject(Path.GetFileName(MyConfig.CopyrightHref));
}
}
// add root page if requested
if (rootPageFileName != null) {
if (!File.Exists(rootPageFileName)) {
throw new DocumenterException("Cannot find the documentation's root page file:\n"
+ rootPageFileName);
}
// add the file
string rootPageOutputName = Path.Combine(buildContext.WorkingDirectory.FullName, "default.html");
if (Path.GetFullPath(rootPageFileName) != Path.GetFullPath(rootPageOutputName)) {
File.Copy(rootPageFileName, rootPageOutputName, true);
File.SetAttributes(rootPageOutputName, FileAttributes.Archive);
}
buildContext.htmlHelp.AddFileToProject(Path.GetFileName(rootPageOutputName));
buildContext.htmlHelp.AddFileToContents(rootPageTOCName,
Path.GetFileName(rootPageOutputName));
// depending on peer setting, make root page the container
if (MyConfig.RootPageContainsNamespaces)
buildContext.htmlHelp.OpenBookInContents();
}
MakeHtmlForAssemblies(buildContext, MyConfig.MergeAssemblies);
foreach (string filename in filesToInclude) {
buildContext.htmlHelp.AddFileToProject(filename);
}
// close root book if applicable
if (rootPageFileName != null) {
if (MyConfig.RootPageContainsNamespaces)
buildContext.htmlHelp.CloseBookInContents();
}
}
private XmlDocument CreateNDocXml(Project project)
{
string tempFileName = null;
try {
// determine temp file name
tempFileName = Path.GetTempFileName();
// Let the Documenter base class do it's thing.
MakeXmlFile(project, new FileInfo(tempFileName));
// Load the XML documentation into DOM and XPATH doc.
using (FileStream tempFile = File.Open(tempFileName, FileMode.Open, FileAccess.Read)) {
XmlDocument xml = new XmlDocument();
xml.Load(tempFile);
return xml;
}
} finally {
if (tempFileName != null && File.Exists(tempFileName)) {
#if DEBUG
File.Copy(tempFileName, MyConfig.OutputDirectory.TrimEnd('\\', '/') + "\\ndoc.xml", true);
#endif
File.Delete(tempFileName);
}
}
}
private void WriteHtmlContentResources(BuildProjectContext buildContext)
{
EmbeddedResources.WriteEmbeddedResources(
GetType().Module.Assembly,
GetType().Namespace + ".css",
buildContext.WorkingDirectory);
// Write the embedded icons to the html output directory
EmbeddedResources.WriteEmbeddedResources(
GetType().Module.Assembly,
GetType().Namespace + ".images",
buildContext.WorkingDirectory);
// Write the embedded scripts to the html output directory
EmbeddedResources.WriteEmbeddedResources(
GetType().Module.Assembly,
GetType().Namespace + ".scripts",
buildContext.WorkingDirectory);
if (((string)MyConfig.AdditionalContentResourceDirectory).Length > 0)
buildContext.CopyToWorkingDirectory(new DirectoryInfo(MyConfig.AdditionalContentResourceDirectory));
// Write the external files (FilesToInclude) to the html output directory
foreach (string srcFilePattern in MyConfig.FilesToInclude.Split('|')) {
if (string.IsNullOrEmpty(srcFilePattern))
continue;
string path = Path.GetDirectoryName(srcFilePattern);
string pattern = Path.GetFileName(srcFilePattern);
// Path.GetDirectoryName can return null in some cases.
// Treat this as an empty string.
if (path == null)
path = string.Empty;
// Make sure we have a fully-qualified path name
if (!Path.IsPathRooted(path))
path = Path.Combine(Environment.CurrentDirectory, path);
// Directory.GetFiles does not accept null or empty string
// for the searchPattern parameter. When no pattern was
// specified, assume all files (*) are wanted.
if (string.IsNullOrEmpty(pattern))
pattern = "*";
foreach (string srcFile in Directory.GetFiles(path, pattern)) {
string dstFile = Path.Combine(buildContext.WorkingDirectory.FullName, Path.GetFileName(srcFile));
File.Copy(srcFile, dstFile, true);
File.SetAttributes(dstFile, FileAttributes.Archive);
filesToInclude.Add(dstFile);
}
}
}
private static void XslTransform(BuildProjectContext buildContext, string stylesheetName, IXPathNavigable xpathNavigable, XsltArgumentList arguments, TextWriter writer, string targetFilename)
{
StyleSheet stylesheet = buildContext.stylesheets[stylesheetName];
try {
stylesheet.Transform(xpathNavigable, arguments, writer);
} catch (XsltException ex) {
throw new DocumenterException(string.Format("XSLT error while writing file {0} using stylesheet {1}({2}:{3}) : {4}", targetFilename, stylesheetName, ex.LineNumber, ex.LinePosition, ex.Message));
}
}
private MsdnDocumenterConfig MyConfig
{
get
{
return (MsdnDocumenterConfig)Config;
}
}
private static WhichType GetWhichType(XmlNode typeNode)
{
WhichType whichType;
switch (typeNode.Name) {
case "class":
whichType = WhichType.Class;
break;
case "interface":
whichType = WhichType.Interface;
break;
case "structure":
whichType = WhichType.Structure;
break;
case "enumeration":
whichType = WhichType.Enumeration;
break;
case "delegate":
whichType = WhichType.Delegate;
break;
default:
whichType = WhichType.Unknown;
break;
}
return whichType;
}
private void MakeHtmlForAssemblies(BuildProjectContext ctx, bool mergeAssemblies)
{
#if DEBUG
int start = Environment.TickCount;
#endif
MakeHtmlForAssembliesSorted(ctx, mergeAssemblies);
#if DEBUG
Trace.WriteLine("Making Html: " + ((Environment.TickCount - start) / 1000.0) + " sec.");
#endif
}
private void MakeHtmlForAssembliesSorted(BuildProjectContext ctx, bool mergeAssemblies)
{
const string defaultNamespace = null;
XmlNodeList assemblyNodes = ctx.SelectNodes("/ndoc:ndoc/ndoc:assembly");
List assemblyNames = new List();
foreach(XmlNode node in assemblyNodes) assemblyNames.Add(GetNodeName(node));
assemblyNames.Sort();
if (mergeAssemblies)
{
// sort namespaces alphabetically except for defaultNamespace, which is always first
string[] namespaces = SortNamespaces(ctx, assemblyNames, defaultNamespace);
MakeHtmlForNamespaces(ctx, null, namespaces);
}
else
{
foreach (string currentAssemblyName in assemblyNames)
{
MakeHtmlForAssembly(ctx, currentAssemblyName);
ctx.htmlHelp.OpenBookInContents();
string[] namespaces = SortNamespaces(ctx, new List( new[] { currentAssemblyName }) , defaultNamespace);
MakeHtmlForNamespaces(ctx, currentAssemblyName, namespaces);
ctx.htmlHelp.CloseBookInContents();
}
}
}
private void MakeHtmlForAssembly(BuildProjectContext ctx, string assemblyName)
{
BuildAssemblyContext actx = new BuildAssemblyContext(ctx, assemblyName);
string fileName = ctx._nameResolver.GetFilenameForAssembly(assemblyName);
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddParam("assembly-name", String.Empty, assemblyName);
TransformAndWriteResult(actx, "assembly", arguments, fileName);
ctx.htmlHelp.AddFileToContents(assemblyName + " Assembly", fileName, HtmlHelpIcon.Page);
}
private void MakeHtmlForNamespaces(BuildProjectContext ctx, string currentAssembly, IList namespaces)
{
int nNodes = namespaces.Count;
bool heirTOC = (MyConfig.NamespaceTOCStyle == TOCStyle.Hierarchical);
int level = 0;
string[] last = new string[0];
BuildAssemblyContext generatorContext = null;
for (int i = 0; i < nNodes; i++) {
OnDocBuildingProgress(i * 100 / nNodes); // TODO (EE): fix calc for !MergeAssemblies mode
string currentNamespace = namespaces[i];
// determine assembly containing this namespace
XmlNodeList namespaceNodes = (currentAssembly==null)
? ctx.SelectNodes(string.Format("/ndoc:ndoc/ndoc:assembly/ndoc:module/ndoc:namespace[@name='{0}']", currentNamespace))
: ctx.SelectNodes(string.Format("/ndoc:ndoc/ndoc:assembly[@name='{0}']/ndoc:module/ndoc:namespace[@name='{1}']", currentAssembly, currentNamespace));
string assemblyName = GetNodeName(ctx.SelectSingleNode(namespaceNodes[0], "ancestor::ndoc:assembly"));
generatorContext = new BuildAssemblyContext(ctx, assemblyName);
if (heirTOC) {
string[] split = currentNamespace.Split('.');
for (level = last.Length; level >= 0 &&
ArrayEquals(split, 0, last, 0, level) == false; level--) {
if (level > last.Length)
continue;
string namespaceName = string.Join(".", last, 0, level);
XmlNodeList typeNodes = GetTypeNodes(ctx, currentAssembly, namespaceName);
MakeHtmlForTypes(generatorContext, typeNodes);
ctx.htmlHelp.CloseBookInContents();
}
if (level < 0)
level = 0;
for (; level < split.Length; level++) {
string namespaceName = string.Join(".", split, 0, level + 1);
if (!namespaces.Contains(namespaceName))
// if (Array.BinarySearch(namespaces, namespaceName) < 0)
MakeHtmlForNamespace(generatorContext, split[level], namespaceName, false);
else
MakeHtmlForNamespace(generatorContext, split[level], namespaceName, true);
ctx.htmlHelp.OpenBookInContents();
}
last = split;
} else {
MakeHtmlForNamespace(generatorContext, currentNamespace, currentNamespace, true);
using (ctx.htmlHelp.OpenBookInContents()) {
XmlNodeList typeNodes = GetTypeNodes(ctx, currentAssembly, currentNamespace);
MakeHtmlForTypes(generatorContext, typeNodes);
}
}
}
if (heirTOC && last.Length > 0) {
for (; level >= 1; level--) {
string ns = string.Join(".", last, 0, level);
XmlNodeList typeNodes = GetTypeNodes(ctx, currentAssembly, ns);
MakeHtmlForTypes(generatorContext, typeNodes);
ctx.htmlHelp.CloseBookInContents();
}
}
OnDocBuildingProgress(100);
}
private static XmlNodeList GetTypeNodes(BuildProjectContext ctx, string assembly, string namespaceName)
{
string xpath = (assembly == null)
? string.Format(
"/ndoc:ndoc/ndoc:assembly/ndoc:module/ndoc:namespace[@name='{0}']/*[local-name()!='documentation' and local-name()!='typeHierarchy']",
namespaceName)
: string.Format(
"/ndoc:ndoc/ndoc:assembly[@name='{0}']/ndoc:module/ndoc:namespace[@name='{1}']/*[local-name()!='documentation' and local-name()!='typeHierarchy']",
assembly,
namespaceName);
XmlNodeList typeNodes = ctx.SelectNodes(xpath);
return typeNodes;
}
private static bool ArrayEquals(string[] array1, int from1, string[] array2, int from2, int count)
{
for (int i = 0; i < count; i++) {
if (array1[from1 + i] != array2[from2 + i])
return false;
}
return true;
}
private static void GetNamespacesFromAssembly(BuildProjectContext buildContext, string assemblyName, NameValueCollection namespaceAssemblies)
{
XmlNodeList namespaceNodes = buildContext.SelectNodes(string.Format("/ndoc:ndoc/ndoc:assembly[@name='{0}']/ndoc:module/ndoc:namespace", assemblyName));
foreach (XmlNode namespaceNode in namespaceNodes) {
string namespaceName = GetNodeName(namespaceNode);
namespaceAssemblies.Add(namespaceName, assemblyName);
}
}
///
/// Add the namespace elements to the output
///
///
/// The namespace
///
///
/// If nested, the namespace part will be the current
/// namespace element being documented
/// The full namespace name being documented
/// If true, the namespace will be documented, if false
/// the node in the TOC will not link to a page
private void MakeHtmlForNamespace(BuildAssemblyContext ctx, string namespacePart, string namespaceName,
bool addDocumentation)
{
// // handle duplicate namespace documentation
// if (ctx.documentedNamespaces.Contains(namespaceName))
// return;
// ctx.documentedNamespaces.Add(namespaceName);
if (addDocumentation) {
string currentAssemblyName = (ctx.MergeAssemblies) ? string.Empty : ctx.CurrentAssemblyName;
string namespaceFilename = ctx._nameResolver.GetFilenameForNamespace(currentAssemblyName, namespaceName);
ctx.htmlHelp.AddFileToContents(namespacePart, namespaceFilename);
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddParam("merge-assemblies", String.Empty, ctx.MergeAssemblies);
arguments.AddParam("namespace", String.Empty, namespaceName);
TransformAndWriteResult(ctx, "namespace", arguments, namespaceFilename);
string namespaceHierarchyFilename = ctx._nameResolver.GetFilenameForNamespaceHierarchy(currentAssemblyName, namespaceName);
arguments = new XsltArgumentList();
arguments.AddParam("merge-assemblies", String.Empty, ctx.MergeAssemblies);
arguments.AddParam("namespace", String.Empty, namespaceName);
TransformAndWriteResult(ctx, "namespacehierarchy", arguments, namespaceHierarchyFilename);
} else {
ctx.htmlHelp.AddFileToContents(namespacePart);
}
}
private void MakeHtmlForTypes(BuildProjectContext projectCtx, XmlNodeList typeNodes)
{
int[] indexes = SortNodesByAttribute(typeNodes, "id");
int nNodes = typeNodes.Count;
for (int i = 0; i < nNodes; i++) {
XmlNode typeNode = typeNodes[indexes[i]];
WhichType whichType = GetWhichType(typeNode);
string assemblyName = XmlUtils.GetNodeName(projectCtx.SelectSingleNode(typeNode, "ancestor::ndoc:assembly"));
BuildAssemblyContext ctx = new BuildAssemblyContext(projectCtx, assemblyName); // TODO (EE): initialize w/ assembly name
switch (whichType) {
case WhichType.Class:
MakeHtmlForInterfaceOrClassOrStructure(ctx, whichType, typeNode);
break;
case WhichType.Interface:
MakeHtmlForInterfaceOrClassOrStructure(ctx, whichType, typeNode);
break;
case WhichType.Structure:
MakeHtmlForInterfaceOrClassOrStructure(ctx, whichType, typeNode);
break;
case WhichType.Enumeration:
MakeHtmlForEnumerationOrDelegate(ctx, whichType, typeNode);
break;
case WhichType.Delegate:
MakeHtmlForEnumerationOrDelegate(ctx, whichType, typeNode);
break;
default:
break;
}
}
}
private void MakeHtmlForEnumerationOrDelegate(BuildAssemblyContext ctx, WhichType whichType, XmlNode typeNode)
{
string typeName = whichType == WhichType.Delegate ? GetNodeDisplayName(typeNode) : GetNodeName(typeNode);
string typeID = GetNodeId(typeNode);
string fileName = ctx._nameResolver.GetFilenameForId(ctx.CurrentAssemblyName, typeID);
ctx.htmlHelp.AddFileToContents(typeName + " " + mixedCaseTypeNames[whichType], fileName, HtmlHelpIcon.Page);
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddParam("type-id", String.Empty, typeID);
TransformAndWriteResult(ctx, "type", arguments, fileName);
}
private void MakeHtmlForInterfaceOrClassOrStructure(BuildAssemblyContext ctx,
WhichType whichType,
XmlNode typeNode)
{
string typeName = GetNodeDisplayName(typeNode);
string typeID = GetNodeId(typeNode);
string fileName = ctx._nameResolver.GetFilenameForId(ctx.CurrentAssemblyName, typeID);
ctx.htmlHelp.AddFileToContents(typeName + " " + mixedCaseTypeNames[whichType], fileName);
bool hasMembers = ctx.SelectNodes(typeNode, "ndoc:constructor|ndoc:field|ndoc:property|ndoc:method|ndoc:operator|ndoc:event").Count > 0;
if (hasMembers) {
ctx.htmlHelp.OpenBookInContents();
}
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddParam("type-id", String.Empty, typeID);
TransformAndWriteResult(ctx, "type", arguments, fileName);
if (ctx.SelectNodes(typeNode, "ndoc:derivedBy").Count > 5) {
fileName = ctx._nameResolver.GetFilenameForTypeHierarchy(ctx.CurrentAssemblyName, typeID);
arguments = new XsltArgumentList();
arguments.AddParam("type-id", String.Empty, typeID);
TransformAndWriteResult(ctx, "typehierarchy", arguments, fileName);
}
if (hasMembers) {
fileName = ctx._nameResolver.GetFilenameForTypeMemberList(ctx.CurrentAssemblyName, typeID);
ctx.htmlHelp.AddFileToContents(typeName + " Members",
fileName,
HtmlHelpIcon.Page);
arguments = new XsltArgumentList();
arguments.AddParam("type-id", String.Empty, typeID);
TransformAndWriteResult(ctx, "allmembers", arguments, fileName);
MakeHtmlForConstructors(ctx, typeNode);
MakeHtmlForFields(ctx, typeNode);
MakeHtmlForProperties(ctx, typeNode);
MakeHtmlForMethods(ctx, typeNode);
MakeHtmlForOperators(ctx, typeNode);
MakeHtmlForEvents(ctx, typeNode);
ctx.htmlHelp.CloseBookInContents();
}
}
private void MakeHtmlForConstructors(BuildAssemblyContext ctx, XmlNode typeNode)
{
string constructorID;
string fileName;
string typeName = GetNodeDisplayName(typeNode);
string typeID = GetNodeId(typeNode);
XmlNodeList constructorNodes = ctx.SelectNodes(typeNode, "ndoc:constructor[@contract!='Static']");
// If the constructor is overloaded then make an overload page.
if (constructorNodes.Count > 1) {
fileName = ctx._nameResolver.GetFilenameForConstructorList(ctx.CurrentAssemblyName, typeID);
ctx.htmlHelp.AddFileToContents(typeName + " Constructor", fileName);
ctx.htmlHelp.OpenBookInContents();
constructorID = constructorNodes[0].Attributes["id"].Value;
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddParam("member-id", String.Empty, constructorID);
TransformAndWriteResult(ctx, "memberoverload", arguments, fileName);
}
foreach (XmlNode constructorNode in constructorNodes) {
constructorID = constructorNode.Attributes["id"].Value;
fileName = ctx._nameResolver.GetFilenameForId(ctx.CurrentAssemblyName, constructorID);
if (constructorNodes.Count > 1) {
XmlNodeList parameterNodes = ctx.SelectNodes(constructorNode, "ndoc:parameter");
ctx.htmlHelp.AddFileToContents(typeName + " Constructor " + GetParamList(parameterNodes), fileName,
HtmlHelpIcon.Page);
} else {
ctx.htmlHelp.AddFileToContents(typeName + " Constructor", fileName, HtmlHelpIcon.Page);
}
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddParam("member-id", String.Empty, constructorID);
TransformAndWriteResult(ctx, "member", arguments, fileName);
}
if (constructorNodes.Count > 1) {
ctx.htmlHelp.CloseBookInContents();
}
XmlNode staticConstructorNode = ctx.SelectSingleNode(typeNode, "ndoc:constructor[@contract='Static']");
if (staticConstructorNode != null) {
constructorID = staticConstructorNode.Attributes["id"].Value;
fileName = ctx._nameResolver.GetFilenameForId(ctx.CurrentAssemblyName, constructorID);
ctx.htmlHelp.AddFileToContents(typeName + " Static Constructor", fileName, HtmlHelpIcon.Page);
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddParam("member-id", String.Empty, constructorID);
TransformAndWriteResult(ctx, "member", arguments, fileName);
}
}
private void MakeHtmlForFields(BuildAssemblyContext ctx, XmlNode typeNode)
{
XmlNodeList fields = ctx.SelectNodes(typeNode, "ndoc:field[not(@declaringType)]");
if (fields.Count > 0) {
//string typeName = typeNode.Attributes["name"].Value;
string typeID = GetNodeId(typeNode);
string fileName = ctx._nameResolver.GetFilenameForFieldList(ctx.CurrentAssemblyName, typeID);
ctx.htmlHelp.AddFileToContents("Fields", fileName);
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddParam("type-id", String.Empty, typeID);
arguments.AddParam("member-type", String.Empty, "field");
TransformAndWriteResult(ctx, "individualmembers", arguments, fileName);
ctx.htmlHelp.OpenBookInContents();
int[] indexes = SortNodesByAttribute(fields, "id");
foreach (int index in indexes) {
XmlNode field = fields[index];
string fieldName = GetNodeName(field);
string fieldID = GetNodeId(field);
fileName = ctx._nameResolver.GetFilenameForId(ctx.CurrentAssemblyName, fieldID);
ctx.htmlHelp.AddFileToContents(fieldName + " Field", fileName, HtmlHelpIcon.Page);
arguments = new XsltArgumentList();
arguments.AddParam("field-id", String.Empty, fieldID);
TransformAndWriteResult(ctx, "field", arguments, fileName);
}
ctx.htmlHelp.CloseBookInContents();
}
}
private void MakeHtmlForProperties(BuildAssemblyContext ctx, XmlNode typeNode)
{
XmlNodeList declaredPropertyNodes = ctx.SelectNodes(typeNode, "ndoc:property[not(@declaringType)]");
if (declaredPropertyNodes.Count > 0) {
XmlNode propertyNode;
bool bOverloaded = false;
int i;
string typeID = GetNodeId(typeNode);
XmlNodeList propertyNodes = ctx.SelectNodes(typeNode, "ndoc:property[not(@declaringType)]");
int nNodes = propertyNodes.Count;
int[] indexes = SortNodesByAttribute(propertyNodes, "id");
string fileName = ctx._nameResolver.GetFilenameForPropertyList(ctx.CurrentAssemblyName, typeID);
ctx.htmlHelp.AddFileToContents("Properties", fileName);
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddParam("type-id", String.Empty, typeID);
arguments.AddParam("member-type", String.Empty, "property");
TransformAndWriteResult(ctx, "individualmembers", arguments, fileName);
ctx.htmlHelp.OpenBookInContents();
for (i = 0; i < nNodes; i++) {
propertyNode = propertyNodes[indexes[i]];
string propertyName = propertyNode.Attributes["name"].Value;
string propertyID = propertyNode.Attributes["id"].Value;
// If the method is overloaded then make an overload page.
string previousPropertyName = ((i - 1 < 0) || (propertyNodes[indexes[i - 1]].Attributes.Count == 0))
? "" : propertyNodes[indexes[i - 1]].Attributes[0].Value;
string nextPropertyName = ((i + 1 == nNodes) || (propertyNodes[indexes[i + 1]].Attributes.Count == 0))
? "" : propertyNodes[indexes[i + 1]].Attributes[0].Value;
if ((previousPropertyName != propertyName) && (nextPropertyName == propertyName)) {
fileName = ctx._nameResolver.GetFilenameForPropertyOverloads(ctx.CurrentAssemblyName, typeID, propertyName);
ctx.htmlHelp.AddFileToContents(propertyName + " Property", fileName);
arguments = new XsltArgumentList();
arguments.AddParam("member-id", String.Empty, propertyID);
TransformAndWriteResult(ctx, "memberoverload", arguments, fileName);
ctx.htmlHelp.OpenBookInContents();
bOverloaded = true;
}
fileName = ctx._nameResolver.GetFilenameForId(ctx.CurrentAssemblyName, propertyID);
string pageTitle;
if (!bOverloaded) {
pageTitle = string.Format("{0} Property", propertyName);
} else {
XmlNodeList parameterNodes = ctx.SelectNodes(propertyNode, "ns:parameter");
pageTitle = string.Format("{0} Property {1}", propertyName, GetParamList(parameterNodes));
}
ctx.htmlHelp.AddFileToContents(pageTitle, fileName, HtmlHelpIcon.Page);
XsltArgumentList arguments2 = new XsltArgumentList();
arguments2.AddParam("property-id", String.Empty, propertyID);
TransformAndWriteResult(ctx, "property", arguments2, fileName);
if ((previousPropertyName == propertyName) && (nextPropertyName != propertyName)) {
ctx.htmlHelp.CloseBookInContents();
bOverloaded = false;
}
}
ctx.htmlHelp.CloseBookInContents();
}
}
private static string GetPreviousMethodName(XmlNodeList methodNodes, int[] indexes, int index)
{
while (--index >= 0) {
if (methodNodes[indexes[index]].Attributes["declaringType"] == null)
return methodNodes[indexes[index]].Attributes["name"].Value;
}
return null;
}
private static string GetNextMethodName(XmlNodeList methodNodes, int[] indexes, int index)
{
while (++index < methodNodes.Count) {
if (methodNodes[indexes[index]].Attributes["declaringType"] == null)
return methodNodes[indexes[index]].Attributes["name"].Value;
}
return null;
}
// returns true, if method is neither overload of a method in the same class,
// nor overload of a method in the base class.
private static bool IsMethodAlone(XmlNodeList methodNodes, int[] indexes, int index)
{
string name = methodNodes[indexes[index]].Attributes["name"].Value;
int lastIndex = methodNodes.Count - 1;
if (lastIndex <= 0)
return true;
bool previousNameDifferent = (index == 0)
|| (methodNodes[indexes[index - 1]].Attributes["name"].Value != name);
bool nextNameDifferent = (index == lastIndex)
|| (methodNodes[indexes[index + 1]].Attributes["name"].Value != name);
return (previousNameDifferent && nextNameDifferent);
}
private static bool IsMethodFirstOverload(XmlNodeList methodNodes, int[] indexes, int index)
{
if ((methodNodes[indexes[index]].Attributes["declaringType"] != null)
|| IsMethodAlone(methodNodes, indexes, index))
return false;
string name = methodNodes[indexes[index]].Attributes["name"].Value;
string previousName = GetPreviousMethodName(methodNodes, indexes, index);
return previousName != name;
}
private static bool IsMethodLastOverload(XmlNodeList methodNodes, int[] indexes, int index)
{
if ((methodNodes[indexes[index]].Attributes["declaringType"] != null)
|| IsMethodAlone(methodNodes, indexes, index))
return false;
string name = methodNodes[indexes[index]].Attributes["name"].Value;
string nextName = GetNextMethodName(methodNodes, indexes, index);
return nextName != name;
}
private void MakeHtmlForMethods(BuildAssemblyContext ctx, XmlNode typeNode)
{
XmlNodeList declaredMethodNodes = ctx.SelectNodes(typeNode, "ndoc:method[not(@declaringType)]");
if (declaredMethodNodes.Count > 0) {
bool bOverloaded = false;
string typeID = GetNodeId(typeNode);
XmlNodeList methodNodes = ctx.SelectNodes(typeNode, "ndoc:method");
int nNodes = methodNodes.Count;
int[] indexes = SortNodesByAttribute(methodNodes, "id");
string fileName = ctx._nameResolver.GetFilenameForMethodList(ctx.CurrentAssemblyName, typeID);
ctx.htmlHelp.AddFileToContents("Methods", fileName);
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddParam("type-id", String.Empty, typeID);
arguments.AddParam("member-type", String.Empty, "method");
TransformAndWriteResult(ctx, "individualmembers", arguments, fileName);
ctx.htmlHelp.OpenBookInContents();
for (int i = 0; i < nNodes; i++) {
XmlNode methodNode = methodNodes[indexes[i]];
string methodDisplayName = GetNodeDisplayName(methodNode);
string methodName = GetNodeName(methodNode);
string methodID = GetNodeId(methodNode);
if (IsMethodFirstOverload(methodNodes, indexes, i)) {
bOverloaded = true;
fileName = ctx._nameResolver.GetFilenameForMethodOverloads(ctx.CurrentAssemblyName, typeID, methodName);
ctx.htmlHelp.AddFileToContents(methodDisplayName + " Method", fileName);
arguments = new XsltArgumentList();
arguments.AddParam("member-id", String.Empty, methodID);
TransformAndWriteResult(ctx, "memberoverload", arguments, fileName);
ctx.htmlHelp.OpenBookInContents();
}
if (XmlUtils.GetAttributeString(methodNode, "declaringType", false) == null) {
fileName = ctx._nameResolver.GetFilenameForId(ctx.CurrentAssemblyName, methodID);
string pageTitle;
if (bOverloaded) {
XmlNodeList parameterNodes = ctx.SelectNodes(methodNode, "ndoc:parameter");
pageTitle = methodDisplayName + GetParamList(parameterNodes) + " Method ";
} else {
pageTitle = methodDisplayName + " Method";
}
ctx.htmlHelp.AddFileToContents(pageTitle, fileName,
HtmlHelpIcon.Page);
XsltArgumentList arguments2 = new XsltArgumentList();
arguments2.AddParam("member-id", String.Empty, methodID);
TransformAndWriteResult(ctx, "member", arguments2, fileName);
}
if (bOverloaded && IsMethodLastOverload(methodNodes, indexes, i)) {
bOverloaded = false;
ctx.htmlHelp.CloseBookInContents();
}
}
ctx.htmlHelp.CloseBookInContents();
}
}
private void MakeHtmlForOperators(BuildAssemblyContext ctx, XmlNode typeNode)
{
XmlNodeList opNodes = ctx.SelectNodes(typeNode, "ndoc:operator");
if (opNodes.Count == 0)
return;
string typeID = GetNodeId(typeNode);
string fileName = ctx._nameResolver.GetFilenameForOperatorList(ctx.CurrentAssemblyName, typeID);
bool bOverloaded = false;
bool bHasOperators =
(ctx.SelectSingleNode(typeNode, "ndoc:operator[@name != 'op_Explicit' and @name != 'op_Implicit']") != null);
bool bHasConverters =
(ctx.SelectSingleNode(typeNode, "ndoc:operator[@name = 'op_Explicit' or @name = 'op_Implicit']") != null);
string pageTitle = "";
if (bHasOperators) {
pageTitle = bHasConverters ? "Operators and Type Conversions" : "Operators";
} else {
if (bHasConverters) {
pageTitle = "Type Conversions";
}
}
ctx.htmlHelp.AddFileToContents(pageTitle, fileName);
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddParam("type-id", String.Empty, typeID);
arguments.AddParam("member-type", String.Empty, "operator");
TransformAndWriteResult(ctx, "individualmembers", arguments, fileName);
ctx.htmlHelp.OpenBookInContents();
int[] indexes = SortNodesByAttribute(opNodes, "id");
int nNodes = opNodes.Count;
//operators first
for (int i = 0; i < nNodes; i++) {
XmlNode operatorNode = opNodes[indexes[i]];
string operatorID = GetNodeId(operatorNode);
string opName = GetNodeName(operatorNode);
if ((opName != "op_Implicit") && (opName != "op_Explicit")) {
if (IsMethodFirstOverload(opNodes, indexes, i)) {
bOverloaded = true;
fileName = ctx._nameResolver.GetFilenameForOperatorOverloads(ctx.CurrentAssemblyName, typeID, opName);
ctx.htmlHelp.AddFileToContents(GetOperatorDisplayName(ctx, operatorNode), fileName);
arguments = new XsltArgumentList();
arguments.AddParam("member-id", String.Empty, operatorID);
TransformAndWriteResult(ctx, "memberoverload", arguments, fileName);
ctx.htmlHelp.OpenBookInContents();
}
fileName = ctx._nameResolver.GetFilenameForId(ctx.CurrentAssemblyName, operatorID);
string opPageTitle;
if (bOverloaded) {
XmlNodeList parameterNodes = ctx.SelectNodes(operatorNode, "ns:parameter");
opPageTitle = GetOperatorDisplayName(ctx, operatorNode) + GetParamList(parameterNodes);
} else {
opPageTitle = GetOperatorDisplayName(ctx, operatorNode);
}
ctx.htmlHelp.AddFileToContents(opPageTitle, fileName,
HtmlHelpIcon.Page);
arguments = new XsltArgumentList();
arguments.AddParam("member-id", String.Empty, operatorID);
TransformAndWriteResult(ctx, "member", arguments, fileName);
if (bOverloaded && IsMethodLastOverload(opNodes, indexes, i)) {
bOverloaded = false;
ctx.htmlHelp.CloseBookInContents();
}
}
}
//type converters
for (int i = 0; i < nNodes; i++) {
XmlNode operatorNode = opNodes[indexes[i]];
string operatorID = GetNodeId(operatorNode);
string opName = GetNodeName(operatorNode);
if ((opName == "op_Implicit") || (opName == "op_Explicit")) {
fileName = ctx._nameResolver.GetFilenameForId(ctx.CurrentAssemblyName, operatorID);
ctx.htmlHelp.AddFileToContents(GetOperatorDisplayName(ctx, operatorNode), fileName,
HtmlHelpIcon.Page);
arguments = new XsltArgumentList();
arguments.AddParam("member-id", String.Empty, operatorID);
TransformAndWriteResult(ctx, "member", arguments, fileName);
}
}
ctx.htmlHelp.CloseBookInContents();
}
private static string GetOperatorDisplayName(BuildProjectContext ctx, XmlNode operatorNode)
{
string name = GetNodeName(operatorNode);
switch (name) {
case "op_Decrement":
return "Decrement Operator";
case "op_Increment":
return "Increment Operator";
case "op_UnaryNegation":
return "Unary Negation Operator";
case "op_UnaryPlus":
return "Unary Plus Operator";
case "op_LogicalNot":
return "Logical Not Operator";
case "op_True":
return "True Operator";
case "op_False":
return "False Operator";
case "op_AddressOf":
return "Address Of Operator";
case "op_OnesComplement":
return "Ones Complement Operator";
case "op_PointerDereference":
return "Pointer Dereference Operator";
case "op_Addition":
return "Addition Operator";
case "op_Subtraction":
return "Subtraction Operator";
case "op_Multiply":
return "Multiplication Operator";
case "op_Division":
return "Division Operator";
case "op_Modulus":
return "Modulus Operator";
case "op_ExclusiveOr":
return "Exclusive Or Operator";
case "op_BitwiseAnd":
return "Bitwise And Operator";
case "op_BitwiseOr":
return "Bitwise Or Operator";
case "op_LogicalAnd":
return "LogicalAnd Operator";
case "op_LogicalOr":
return "Logical Or Operator";
case "op_Assign":
return "Assignment Operator";
case "op_LeftShift":
return "Left Shift Operator";
case "op_RightShift":
return "Right Shift Operator";
case "op_SignedRightShift":
return "Signed Right Shift Operator";
case "op_UnsignedRightShift":
return "Unsigned Right Shift Operator";
case "op_Equality":
return "Equality Operator";
case "op_GreaterThan":
return "Greater Than Operator";
case "op_LessThan":
return "Less Than Operator";
case "op_Inequality":
return "Inequality Operator";
case "op_GreaterThanOrEqual":
return "Greater Than Or Equal Operator";
case "op_LessThanOrEqual":
return "Less Than Or Equal Operator";
case "op_UnsignedRightShiftAssignment":
return "Unsigned Right Shift Assignment Operator";
case "op_MemberSelection":
return "Member Selection Operator";
case "op_RightShiftAssignment":
return "Right Shift Assignment Operator";
case "op_MultiplicationAssignment":
return "Multiplication Assignment Operator";
case "op_PointerToMemberSelection":
return "Pointer To Member Selection Operator";
case "op_SubtractionAssignment":
return "Subtraction Assignment Operator";
case "op_ExclusiveOrAssignment":
return "Exclusive Or Assignment Operator";
case "op_LeftShiftAssignment":
return "Left Shift Assignment Operator";
case "op_ModulusAssignment":
return "Modulus Assignment Operator";
case "op_AdditionAssignment":
return "Addition Assignment Operator";
case "op_BitwiseAndAssignment":
return "Bitwise And Assignment Operator";
case "op_BitwiseOrAssignment":
return "Bitwise Or Assignment Operator";
case "op_Comma":
return "Comma Operator";
case "op_DivisionAssignment":
return "Division Assignment Operator";
case "op_Explicit": {
XmlNode parameterNode = ctx.SelectSingleNode(operatorNode, "ndoc:parameter");
string from = GetNodeTypeId(parameterNode);
string to = GetNodeTypeId(ctx.SelectSingleNode(operatorNode, "ndoc:returnType"));
return "Explicit " + StripNamespace(from) + " to " + StripNamespace(to) + " Conversion";
}
case "op_Implicit": {
XmlNode parameterNode = ctx.SelectSingleNode(operatorNode, "ndoc:parameter");
string from = GetNodeTypeId(parameterNode);
string to = GetNodeTypeId(ctx.SelectSingleNode(operatorNode, "ndoc:returnType"));
return "Implicit " + StripNamespace(from) + " to " + StripNamespace(to) + " Conversion";
}
default:
return "ERROR";
}
}
private void MakeHtmlForEvents(BuildAssemblyContext ctx, XmlNode typeNode)
{
XmlNodeList declaredEventNodes = ctx.SelectNodes(typeNode, "ndoc:event[not(@declaringType)]");
if (declaredEventNodes.Count > 0) {
XmlNodeList events = ctx.SelectNodes(typeNode, "ns:event");
if (events.Count > 0) {
//string typeName = (string)typeNode.Attributes["name"].Value;
string typeID = GetNodeId(typeNode);
string fileName = ctx._nameResolver.GetFilenameForEventList(ctx.CurrentAssemblyName, typeID);
ctx.htmlHelp.AddFileToContents("Events", fileName);
XsltArgumentList arguments = new XsltArgumentList();
arguments.AddParam("type-id", String.Empty, typeID);
arguments.AddParam("member-type", String.Empty, "event");
TransformAndWriteResult(ctx, "individualmembers", arguments, fileName);
ctx.htmlHelp.OpenBookInContents();
int[] indexes = SortNodesByAttribute(events, "id");
foreach (int index in indexes) {
XmlNode eventElement = events[index];
if (XmlUtils.GetAttributeString(eventElement, "declaringType", false) == null) {
string eventName = GetNodeName(eventElement);
string eventID = GetNodeId(eventElement);
fileName = ctx._nameResolver.GetFilenameForId(ctx.CurrentAssemblyName, eventID);
ctx.htmlHelp.AddFileToContents(eventName + " Event",
fileName,
HtmlHelpIcon.Page);
arguments = new XsltArgumentList();
arguments.AddParam("event-id", String.Empty, eventID);
TransformAndWriteResult(ctx, "event", arguments, fileName);
}
}
ctx.htmlHelp.CloseBookInContents();
}
}
}
private static string GetParamList(XmlNodeList parameterNodes)
{
ArrayList parameters = new ArrayList();
foreach (XmlNode parameterNode in parameterNodes) {
string parameterTypeName = GetParameterTypeName(parameterNode, "displayName");
parameters.Add(parameterTypeName);
}
string[] parameterTypeNames = (string[]) parameters.ToArray(typeof (string));
string paramList = "(" + string.Join(",", parameterTypeNames) + ")";
return paramList;
}
private static string GetParameterTypeName(XmlNode root, string typeAttributeName)
{
XmlAttribute typeAtt = root.Attributes[typeAttributeName];
return typeAtt.Value;
}
private static string GetNodeId(XmlNode node)
{
return XmlUtils.GetNodeId(node);
}
private static string GetNodeTypeId(XmlNode node)
{
return XmlUtils.GetNodeTypeId(node);
}
private static string GetNodeName(XmlNode node)
{
return XmlUtils.GetNodeName(node);
}
private static string GetNodeDisplayName(XmlNode node)
{
return XmlUtils.GetNodeDisplayName(node);
}
private static string StripNamespace(string name)
{
string result = name;
int lastDot = name.LastIndexOf('.');
if (lastDot != -1) {
result = name.Substring(lastDot + 1);
}
return result;
}
private static int[] SortNodesByAttribute(XmlNodeList nodes, string attributeName)
{
int length = nodes.Count;
string[] names = new string[length];
int[] indexes = new int[length];
int i = 0;
foreach (XmlNode node in nodes) {
names[i] = node.Attributes[attributeName].Value;
indexes[i] = i++;
}
Array.Sort(names, indexes);
return indexes;
}
private static string[] SortNamespaces(BuildProjectContext ctx, IList assemblyNames, string defaultNamespace)
{
NameValueCollection namespaceAssemblies = new NameValueCollection();
int nNodes = assemblyNames.Count;
for (int i = 0; i < nNodes; i++) {
string assemblyName = assemblyNames[i];
GetNamespacesFromAssembly(ctx, assemblyName, namespaceAssemblies);
}
string[] namespaces = namespaceAssemblies.AllKeys;
if (string.IsNullOrEmpty(defaultNamespace)) {
Array.Sort(namespaces);
} else {
Array.Sort(namespaces, (x, y) =>
{
if (x == y) {
return 0;
} else if (x == null || x == defaultNamespace) {
return -1;
} else if (y == defaultNamespace) {
return 1;
}
return x.CompareTo(y);
});
}
return namespaces;
}
private void TransformAndWriteResult(BuildAssemblyContext ctx,
string transformName,
XsltArgumentList arguments,
string filename)
{
Trace.WriteLine(filename);
#if DEBUG
int start = Environment.TickCount;
#endif
ExternalHtmlProvider htmlProvider = new ExternalHtmlProvider(MyConfig, filename);
try {
StreamWriter streamWriter;
string fullPath = Path.Combine(ctx.WorkingDirectory.FullName, filename);
using (streamWriter = new StreamWriter(
File.Open(fullPath, FileMode.Create),
ctx.CurrentFileEncoding)) {
string DocLangCode = Enum.GetName(typeof(SdkLanguage), MyConfig.SdkDocLanguage).Replace("_", "-");
MsdnXsltUtilities utilities = new MsdnXsltUtilities(ctx._nameResolver, ctx.CurrentAssemblyName, MyConfig.SdkDocVersionString, DocLangCode, MyConfig.SdkLinksOnWeb, ctx.CurrentFileEncoding);
ctx._nameResolver.utilities = utilities;
if (arguments.GetParam("assembly-name", string.Empty) == null) {
arguments.AddParam("assembly-name", String.Empty, ctx.CurrentAssemblyName);
}
arguments.AddParam("ndoc-title", String.Empty, MyConfig.Title);
arguments.AddParam("ndoc-vb-syntax", String.Empty, MyConfig.ShowVisualBasic);
arguments.AddParam("ndoc-omit-object-tags", String.Empty, ((MyConfig.OutputTarget & OutputType.HtmlHelp) == 0));
arguments.AddParam("ndoc-document-attributes", String.Empty, MyConfig.DocumentAttributes);
arguments.AddParam("ndoc-documented-attributes", String.Empty, MyConfig.DocumentedAttributes);
arguments.AddParam("ndoc-sdk-doc-base-url", String.Empty, utilities.SdkDocBaseUrl);
arguments.AddParam("ndoc-sdk-doc-file-ext", String.Empty, utilities.SdkDocExt);
arguments.AddParam("ndoc-sdk-doc-language", String.Empty, utilities.SdkDocLanguage);
arguments.AddExtensionObject("urn:NDocUtil", utilities);
arguments.AddExtensionObject("urn:NDocExternalHtml", htmlProvider);
//Use new overload so we don't get obsolete warnings - clean compile :)
XslTransform(ctx, transformName, ctx.GetXPathNavigable(), arguments, streamWriter, fullPath);
}
}
catch(IOException ex)
{
throw new DocumenterException(string.Format("IO error while creating file {0}", filename), ex);
}
// catch (PathTooLongException e) {
// throw new PathTooLongException(e.Message + "\nThe file that NDoc3 was trying to create had the following name:\n" + Path.Combine(ctx.WorkingDirectory.FullName, filename));
// }
#if DEBUG
Debug.WriteLine((Environment.TickCount - start) + " msec.");
#endif
ctx.htmlHelp.AddFileToProject(filename);
}
}
}