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