/* Copyright (c) Eric Ledoux. All rights reserved. */ /* See http://www.dwell.net/terms for code sharing information. */ // App.cs // // Application entry point. // using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Web.UI; using System.Xml; using System.Xml.Schema; using DwellNet.CodeDoc; using CodeDoc.Properties; // TODO (deferred): change code to use typed XmlReader calls to read the // CodeDoc XML project file, now that there's an XML schema defined namespace DwellNet.CodeDoc.Application {
App Class
Created documentation from source code. |
class App { ////////////////////////////////////////////////////////////////////////// // Private Fields - Maintained Across Multiple CodeDoc XML Project Files //
App.m_warningCount Field
The number of warning messages displayed. |
int m_warningCount;
App.m_exePath Field
The full path of this .exe file. |
string m_exePath;
App.s_app Field
For debugging purposes. |
static App s_app;
App.s_docBrowserSupportFiles Field
The names of static (i.e. non-generated) files that are used in DocBrowser (i.e. what WriteDocumentation() writes) |
static readonly string[] s_docBrowserSupportFiles = new string[] { "_1px.gif", "_Closed.gif", "_CodeDoc.css", "_DocBrowser.js", "_Leaf.gif", "_Maximize.gif", "_Opened.gif", "_SyncToc.gif", "_SyncToc.htm", "_TabLeft.gif", "_TabRight.gif" };
App.s_formattedCodeSupportFiles Field
The names of static (i.e. non-generated) files that are used in the HTML files that WriteFormattedCode() generates. |
static readonly string[] s_formattedCodeSupportFiles = new string[] { "_CodeDoc.css" }; ////////////////////////////////////////////////////////////////////////// // Private Fields - Reset For Each CodeDoc XML Project File //
App.m_projectFilePath Field
The full path to the CodeDoc XML project file currently being processed, or null if none. |
string m_projectFilePath;
App.m_supportFilesPath Field
The full path to directory containing CodeDoc support files. |
string m_supportFilesPath;
App.m_docSet Field
The documentation set being generated. Consists of one or more source code files. |
DocumentationSet m_docSet;
App.m_documentedTopicCount Field
The count of documented topics. |
int m_documentedTopicCount;
App.m_overloadLists Field
When a DocumentationSet.DuplicateCanonicalName event is fired, an entry is added or updated in m_overloadLists with the key being the canonical name and the value being the number of overloads for that canonical name. |
Dictionary<string,int> m_overloadLists;
App.m_referencesNotFound Field
The list of potential references from one topic to another that were not found. These do not necessarily indicate errors. |
List<ReferenceNotFound> m_referencesNotFound;
App.m_externalReferences Field
The contents of the <ExternalReferences> section of the CodeDoc XML project file. Each line of that section is a key in this dictionary; the values are all true. These are external references, which means that they are not reported as "references not found". |
Dictionary<string, bool> m_externalReferences;
App.m_verbose Field
The verbosity level. 0 is minimal output, positive integers produce more output. |
int m_verbose;
App.m_docBrowserNodes Field
The collection of DocBrowserNode objects that have NodeID values; the key is the NodeId value. A value may be DocBrowser object (i.e. the root of a DocBrowser tree) or a regular DocBrowserNode object. |
Dictionary<string, DocBrowserNode> m_docBrowserNodes;
bool m_processedTopics; ////////////////////////////////////////////////////////////////////////// // Private Properties //
App.FalseTrue Property
An IEnumerable which yields false and then true. |
IEnumerable<bool> FalseTrue { get { yield return false; yield return true; } } ////////////////////////////////////////////////////////////////////////// // Methods //
App.Main Method
Main entry point for the application.
Parameters
args Command-line arguments. |
public static int Main(string[] args) { bool pauseOnExit = false; // true if "/Pause" specifed on command line bool success; try { s_app = new App(); success = s_app.Run(args, ref pauseOnExit); } catch (UsageException) { // display command-line usage information; in a debug build include // debug-only options Console.Error.WriteLine(Resources.Usage, #if DEBUG Resources.DebugUsage #else String.Empty #endif ); return 2; } #if DEBUG catch (UnitTests.TestFailedException ex) { Console.Error.WriteLine("\n" + Resources.TestsFailed, ex.Message); return 2; } #endif #if !DEBUG catch (Exception ex) { Console.Error.WriteLine("\n" + Resources.Error, ex.Message); return 2; } #endif // execute "/Pause" command-line argument, if present if (pauseOnExit) { Console.Error.WriteLine(Resources.PressAnyKey); Console.ReadKey(false); } // done; return exit code 1 if there were warnings return (success ? 0 : 1); }
App.Run Method
Executes the application.
Parameters
args Command-line arguments. pauseOnExit Set to true if the "/Pause" command-line argument is given. The caller should initialize this parameter to false.
Return Value
true if the run succeeded with no warning messages, false if one or more warnings were displayed. |
public bool Run(string[] args, ref bool pauseOnExit) { // initialize <m_exePath> to be the full path of this .exe file. m_exePath = System.Reflection.Assembly.GetExecutingAssembly().Location; // parse command-line arguments System.Collections.IEnumerator argEnum = args.GetEnumerator(); while (argEnum.MoveNext()) { string arg = (string) argEnum.Current; #if DEBUG Match match; #endif if (arg == "/Pause") { pauseOnExit = true; } #if DEBUG else if ((match = Regex.Match(arg, @"/RunTests(?::([A-Za-z0-9,]+))?")) .Success) { string[] tests = match.Groups[1].Value.Split( new char[] {','}, StringSplitOptions.RemoveEmptyEntries); bool unused; string testDataDirPath = MakePath(NextArg(argEnum), FileFlags.Directory, out unused); UnitTests.RunTests(testDataDirPath, tests); } else if (arg == "/UpdateTests") { bool unused; string testDataDirPath = MakePath(NextArg(argEnum), FileFlags.Directory, out unused); UnitTests.UpdateTests(testDataDirPath); } else if (arg == "/Verbose") { m_verbose = NextArgAsInt(argEnum); } #endif else if (arg == "/?") throw new UsageException(); else { // <arg> should be the name of a command file -- process it XmlSchema xmlSchema; using (XmlReader xmlReader = XmlReader.Create( App.GetPath("CodeDoc.xsd", m_exePath))) { xmlSchema = XmlSchema.Read(xmlReader, delegate(object sender, ValidationEventArgs e) { Debug.Fail("Error in CodeDoc.xsd", e.Message); }); } XmlReaderSettings xmlSettings = new XmlReaderSettings(); xmlSettings.Schemas.Add(xmlSchema); xmlSettings.ValidationType = ValidationType.Schema; using (XmlReader xmlReader = XmlReader.Create(arg, xmlSettings)) { try { // initialize the state of <this> for this CodeDoc XML // project file m_projectFilePath = Path.GetFullPath(arg); m_docSet = new DocumentationSet(); InitializeDocumentationSet(); m_documentedTopicCount = 0; m_overloadLists = new Dictionary<string,int>(50); m_referencesNotFound = new List<ReferenceNotFound>(100); m_externalReferences = DefaultCSharpExternalReferences(); new Dictionary<string,bool>(100); m_verbose = 1; m_docBrowserNodes = new Dictionary<string,DocBrowserNode>(); m_processedTopics = false; // process the CodeDoc XML project file ProcessProjectFile(xmlReader); // to reduce bugs, clear most of the state that only // makes sense in the context of a given project file m_projectFilePath = null; m_supportFilesPath = null; m_docSet = null; m_overloadLists = null; m_referencesNotFound = null; m_externalReferences = null; } catch (XmlSchemaValidationException ex) { throw new ProjectFileException("{0}({1}): {2}", arg, ((IXmlLineInfo)xmlReader).LineNumber, ex.Message); } #if false catch (XmlException ex) { throw new ProjectFileException("{0}: {1}", arg, ex.Message); } #endif catch (ProjectFileException ex) { throw new ProjectFileException("{0}({1}): {2}", arg, ((IXmlLineInfo)xmlReader).LineNumber, ex.Message); } catch (BegunUsingSourcesException ex) { throw new ProjectFileException("{0}({1}): {2}", arg, ((IXmlLineInfo)xmlReader).LineNumber, ex.Message); } } } } // provide feedback on warnings Console.WriteLine(Resources.Done, m_warningCount); if ((m_referencesNotFound != null) && (m_referencesNotFound.Count > 0) && (m_verbose < 1)) Console.Error.WriteLine(Resources.TipReferencesNotFound); // done return (m_warningCount == 0); }
App.InitializeDocumentationSet Method
Initializes m_docSet. |
public void InitializeDocumentationSet() { // hook up <m_docSet> events m_docSet.AddingSourceFile += new AddingSourceFileEventDelegate(m_docSet_AddingSourceFile); m_docSet.AddedSourceFile += new AddedSourceFileEventDelegate(m_docSet_AddedSourceFile); m_docSet.ParsingError += new ParsingErrorEventDelegate(m_docSet_ParsingError); m_docSet.DuplicateTopicTitle += new DuplicateTopicTitleEventDelegate(m_docSet_DuplicateTopicTitle); m_docSet.DuplicateCanonicalName += new DuplicateCanonicalNameEventDelegate( m_docSet_DuplicateCanonicalName); m_docSet.ReferenceNotFound += new ReferenceNotFoundEventDelegate(m_docSet_ReferenceNotFound); }
App.DefaultCSharpExternalReferences Method
Returns a collection of default external references for the C# languages; for example, predefined types like "int". May be used to initialize m_externalReferences. |
Dictionary<string,bool> DefaultCSharpExternalReferences() { string[] wordsArray = new string[] { "bool", "byte", "char", "decimal", "double", "fixed", "float", "int", "long", "object", "sbyte", "short", "string", "uint", "ulong", "ushort", "void" }; Dictionary<string, bool> words = new Dictionary<string, bool>(wordsArray.Length); foreach (string word in wordsArray) words.Add(word, true); return words; }
App.ProcessProjectFile Method
Performs the operations specified in a CodeDoc XML project file.
Parameters
xmlReader An XmlReader which, on entry, is positioned at the beginning of the CodeDoc XML project file. |
void ProcessProjectFile(XmlReader xmlReader) { // advance to the root "<CodeDoc>" XML element if (!xmlReader.ReadToFollowing("CodeDoc")) throw new ProjectFileException(Resources.ProjectFileMissingRoot); if (xmlReader.IsEmptyElement) return; // empty "<CodeDoc/>" --> nothing to do // parse attributes on the "<CodeDoc>" element string pathFromExe = GetAttribute(xmlReader, "SupportFilesPathFromExe", "SupportFiles"); bool unused; m_supportFilesPath = MakePath(GetPath(pathFromExe, m_exePath), FileFlags.Directory, out unused); // parse XML elements under "<CodeDoc>" while (xmlReader.Read()) { if (xmlReader.NodeType == XmlNodeType.Element) { switch (xmlReader.Name) { case "ExternalReferences": // populate <m_externalReferences> using lines of text from // <ExternalReferences>...</ExternalReferences> xmlReader.Read(); xmlReader.MoveToContent(); foreach (string line in xmlReader.Value.Split( new char[] { '\n' })) { string reference = line.Trim(); if (reference.Length > 0) m_externalReferences[reference] = true; } xmlReader.Read(); break; case "DefinedSymbols": // call DocumentationSet.DefineSymbol() using lines of // text from <DefinedSymbols>...</DefinedSymbols> m_docSet.ClearSymbols(); xmlReader.Read(); xmlReader.MoveToContent(); foreach (string line in xmlReader.Value.Split( new char[] { '\n' })) { string symbol = line.Trim(); if (symbol.Length > 0) m_docSet.DefineSymbol(symbol); } xmlReader.Read(); break; case "AllowXmlElements": // call DocumentationSet.AllowXmlElement() using lines of // text from <AllowXmlElements>...</AllowXmlElements> m_docSet.ClearAllowedXmlElements(); xmlReader.Read(); xmlReader.MoveToContent(); foreach (string line in xmlReader.Value.Split( new char[] { '\n' })) { string elementName = line.Trim(); if (elementName.Length > 0) m_docSet.AllowXmlElement(elementName); } xmlReader.Read(); break; case "DeleteFilesInDirectory": DeleteFilesInDirectory(xmlReader); break; case "CreateDocBrowser": // begin a DocBrowser CreateDocBrowser(xmlReader); break; case "DocBrowserStaticPages": // add static content to a DocBrowser DocBrowserStaticPages(xmlReader); break; case "ReadSource": // add source code from the file or directory specified by // the "Path" attribute to the documentation set ReadSource(xmlReader); break; case "ClearSources": // remove all source files from the documentation set m_docSet.ClearSourceFiles(); break; case "WriteDocumentation": // generate documentation from the source files in the // documentation set WriteDocumentation(xmlReader); break; case "WriteFormattedCode": // generate code HTML from the source files in the // documentation set WriteFormattedCode(xmlReader); break; default: throw new ProjectFileException( Resources.ProjectFileUnknownCommand, xmlReader.Name); } } else if (xmlReader.NodeType == XmlNodeType.Text) throw UnexpectedText(xmlReader); } // finish generating each DocBrowser that was begun with a // "<CreateDocBrowser>" element in the CodeDoc XML project file foreach (KeyValuePair<string,DocBrowserNode> kvp in m_docBrowserNodes) { DocBrowserNode docBrowserNode = kvp.Value; DocBrowser docBrowser = docBrowserNode as DocBrowser; if (docBrowser != null) FinishDocBrowser(docBrowser); } // list references not found, sorted by reference name m_referencesNotFound.Sort(); List<ReferenceNotFound>.Enumerator listEnum = m_referencesNotFound.GetEnumerator(); ReferenceNotFound previous = null; int count = 0; List<string> topicsNotFound = new List<string>(20); while (true) { bool atEnd = !listEnum.MoveNext(); if ((previous != null) && (atEnd || (listEnum.Current.Reference != previous.Reference) || (listEnum.Current.IsTypeOnly != previous.IsTypeOnly))) { string message = String.Format(Resources.ReferencesNotFound, previous, count); Console.Error.WriteLine(message); if (m_verbose >= 1) { // at high enough verbosity level, display the context // topics topicsNotFound.Sort(); foreach (string location in topicsNotFound) { Console.Error.WriteLine(Resources.ReferenceNotFound, location, previous); } } count = 1; topicsNotFound.Clear(); } else count++; if (atEnd) break; previous = listEnum.Current; if ((previous.LineNumber > 0) && (previous.ContextTopic.SourceFile != null)) { topicsNotFound.Add(String.Format("{0}({1})", previous.ContextTopic.SourceFile.SourceFileAbsolutePath, previous.LineNumber)); } else topicsNotFound.Add(previous.ContextTopic.Location); } // at a high enough verbosity level, summarize the duplicate overloads // (i.e. the results from DuplicateCanonicalName events) if (m_verbose >= 3) { List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(m_overloadLists.Count); foreach (KeyValuePair<string, int> kvp in m_overloadLists) list.Add(kvp); list.Sort(delegate(KeyValuePair<string, int> kvp1, KeyValuePair<string, int> kvp2) { return String.Compare(kvp1.Key, kvp2.Key); }); foreach (KeyValuePair<string, int> kvp in list) { string message = String.Format(Resources.OverloadsDetected, kvp.Key, kvp.Value); Console.Error.WriteLine(message); } } // at a high enough verbosity level, output a list of all documented // topics, excluding namespaces and overloads if (m_verbose >= 4) { Console.WriteLine(); Console.WriteLine(Resources.DocumentedMembers); foreach (Topic topic in m_docSet.Topics) { if (topic.IsDocumented && (topic.TopicKind != TopicKind.NamespaceTopic) && (topic.OverloadListTopic == null)) { Console.WriteLine(topic.QualifiedMemberName); } } Console.WriteLine(); } }
App.DeleteFilesInDirectory Method
Executes a "<DeleteFilesInDirectory>" CodeDoc XML project file command: deletes all files in a given directory, if that directory exists. Does not delete subdirectories, or files in subdirectories.
Parameters
xmlReader An XmlReader which, on entry, is positioned on a "<DeleteFilesInDirectory>" element within the CodeDoc XML project file. On exit, xmlReader must be positioned on the "</DeleteFilesInDirectory>" element (or, equivalently, "<DeleteFilesInDirectory/>"). |
void DeleteFilesInDirectory(XmlReader xmlReader) { // set <directoryPath> to the full path to the directory to delete the // files of; do nothing if the directory doesn't exist bool directoryExists; string directoryPath = MakePath(GetPath(xmlReader, "Path", m_projectFilePath), FileFlags.Directory | FileFlags.Nonexistent, out directoryExists); if (!directoryExists) return; // delete all files in the directory foreach (string filePath in Directory.GetFiles(directoryPath)) { try { File.Delete(filePath); } catch (Exception) { // give the user extra information, i.e. the line number within // the CodeDoc XML project file of the // "<DeleteFilesInDirectory>" element, before re-throwing the // exception Console.WriteLine(Resources.DeleteFileError, m_projectFilePath, ((IXmlLineInfo)xmlReader).LineNumber); throw; } } }
App.CreateDocBrowser Method
Executes a "<CreateDocBrowser>" CodeDoc XML project file command: begins a DocBrowser.
Parameters
xmlReader An XmlReader which, on entry, is positioned on a "<CreateDocBrowser>" element within the CodeDoc XML project file. On exit, xmlReader must be positioned on the "</CreateDocBrowser>" element (or, equivalently, "<CreateDocBrowser/>"). |
void CreateDocBrowser(XmlReader xmlReader) { // set <rootPath> to the full path to DocBrowser root directory // (i.e. the directory into which the frameset files will be written) bool unused; string rootPath = MakePath( GetPath(xmlReader, "Path", m_projectFilePath), FileFlags.Directory | FileFlags.Nonexistent, out unused); CreateDirectoryIfNeeded(rootPath); // parse other attributes on the "<CreateDocBrowser>" element string pagesRelativePath = GetAttribute(xmlReader, "PagesRelativePath", null); string framesetFileName = GetAttribute(xmlReader, "FramesetFileName", null); string basicFramesetFileName = GetAttribute(xmlReader, "BasicFramesetFileName", null); string nodeId = GetAttribute(xmlReader, "NodeId"); string baseName = GetAttribute(xmlReader, "BaseName", "Default"); string title = GetAttribute(xmlReader, "Title", baseName); int leftFrameWidth = GetAttribute<int>(xmlReader, "LeftFrameWidth", 300); bool markOfTheWeb = GetAttribute<bool>(xmlReader, "MarkOfTheWeb", true); // set <docBrowser> to a new DocBrowser object corresponding to this // XML node DocBrowser docBrowser = new DocBrowser(nodeId, rootPath, pagesRelativePath, framesetFileName, basicFramesetFileName, title, baseName, leftFrameWidth, markOfTheWeb); // create the pages directory if it doesn't exist already CreateDirectoryIfNeeded(docBrowser.PagesAbsolutePath); // parse the contents of the "<CreateDocBrowser>" element, if any if (!xmlReader.IsEmptyElement) { int initialDepth = xmlReader.Depth; while (xmlReader.Read() && (xmlReader.Depth != initialDepth)) { if (xmlReader.NodeType == XmlNodeType.Element) { if (xmlReader.Name == "CopyFile") { string sourcePath = GetPath(xmlReader, "Path", m_projectFilePath); string destinationFileName = GetAttribute(xmlReader, "NewName", Path.GetFileName(sourcePath)); CopyStaticFile(sourcePath, destinationFileName, docBrowser.PagesAbsolutePath, xmlReader); } else { throw new ProjectFileException( Resources.ProjectFileUnexpectedElement, xmlReader.Name); } } else if (xmlReader.NodeType == XmlNodeType.Text) throw UnexpectedText(xmlReader); } } // create the DocBrowser pages directory CreateDirectoryIfNeeded(docBrowser.PagesAbsolutePath); // add <docBrowser> to <m_docBrowserNodes> m_docBrowserNodes[nodeId] = docBrowser; }
App.FinishDocBrowser Method
Finishes generating each DocBrowser that was begun with a "<CreateDocBrowser>" element in the CodeDoc XML project file.
Parameters
docBrowser The DocBrowser to finish. |
void FinishDocBrowser(DocBrowser docBrowser) { // provide user feedback Console.WriteLine(Resources.WritingDocBrowser, docBrowser.NodeId); // copy support files into the DocBrowser pages directory foreach (string fileName in s_docBrowserSupportFiles) { string originalFilePath = Path.Combine(m_supportFilesPath, fileName); string newFilePath = Path.Combine(docBrowser.PagesAbsolutePath, fileName); try { CopyFileContents(originalFilePath, newFilePath, false); } catch (Exception) { // give the user extra information, i.e. the line number within // the CodeDoc XML project file of the "<DocBrowserNode>" // element, before re-throwing the exception Console.WriteLine( Resources.ErrorCopyingDocBrowserSupportFile, docBrowser.NodeId, fileName); throw; } } // write the DocBrowser root frameset HTML file; we actually write two // files: the regular DHTML version and a basic (downlevel) version string outputFileName, outputPath; foreach (bool basic in FalseTrue) { outputFileName = (basic ? docBrowser.BasicFramesetFileName : docBrowser.FramesetFileName); if (outputFileName == null) continue; // no frameset file requested in project file outputPath = Path.Combine(docBrowser.RootPath, outputFileName); using (StreamWriter streamWriter = new StreamWriter(outputPath)) { using (HtmlTextWriterHelper htmlWriter = new HtmlTextWriterHelper(streamWriter)) { WriteDocBrowserFrameset(docBrowser, basic, htmlWriter); } } } // write <root-name>_Tabs.htm (implements the tabs) -- not used in the // basic version outputFileName = docBrowser.TabsHtmlFileName; outputPath = Path.Combine(docBrowser.PagesAbsolutePath, outputFileName); using (StreamWriter streamWriter = new StreamWriter(outputPath)) { // copy DocBrowserTabs.htm to <outputFileName>; prepend the /// "Mark of the Web", if specified if (docBrowser.MarkOfTheWeb) { streamWriter.WriteLine( HtmlTextWriterHelper.MarkOfTheWebHtml); } string path = Path.Combine(m_supportFilesPath, "DocBrowserTabs.htm"); streamWriter.Write(File.ReadAllText(path)); } // write <root-name>_Contents.htm (the "Contents" tab) int topicCount = 0; foreach (bool basic in FalseTrue) { outputFileName = (basic ? Resources.BasicDocBrowserPrefix : String.Empty) + docBrowser.ContentsHtmlFileName; outputPath = Path.Combine(docBrowser.PagesAbsolutePath, outputFileName); using (StreamWriter streamWriter = new StreamWriter(outputPath)) { using (HtmlTextWriterHelper htmlWriter = new HtmlTextWriterHelper(streamWriter)) { topicCount = WriteDocBrowserContentsHtml(docBrowser, basic, htmlWriter); } } } // sort index entries and remove duplicate index entries docBrowser.SortIndexEntries(); docBrowser.RemoveDuplicateIndexEntries(); // write <root-name>_Index.htm (the "Index" tab) foreach (bool basic in FalseTrue) { outputFileName = (basic ? Resources.BasicDocBrowserPrefix : String.Empty) + docBrowser.IndexHtmlFileName; outputPath = Path.Combine(docBrowser.PagesAbsolutePath, outputFileName); using (StreamWriter streamWriter = new StreamWriter(outputPath)) { using (HtmlTextWriterHelper htmlWriter = new HtmlTextWriterHelper(streamWriter)) { WriteDocBrowserIndexHtml(docBrowser, basic, topicCount, htmlWriter); } } } // write <root-name>_Index.xml (an XML version of the index) outputPath = Path.Combine(docBrowser.PagesAbsolutePath, docBrowser.IndexXmlFileName); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.IndentChars = "\t"; using (XmlWriter xmlWriter = XmlWriter.Create(outputPath)) WriteDocBrowserIndexXml(docBrowser, xmlWriter); }
App.DocBrowserStaticPages Method
Executes a "<DocBrowserStaticPages>" CodeDoc XML project file command: adds static (non-generated) content to a DocBrowser.
Parameters
xmlReader An XmlReader which, on entry, is positioned on a "<DocBrowserStaticPages>" element within the CodeDoc XML project file. On exit, xmlReader must be positioned on the "</DocBrowserStaticPages>" element (or, equivalently, "<DocBrowserStaticPages/>"). |
void DocBrowserStaticPages(XmlReader xmlReader) { // parse attributes on the "<DocBrowserStaticPages>" element string nodeId = GetAttribute(xmlReader, "NodeId"); // set <parentDocBrowserNode> to the DocBrowserNode that child // "<DocBrowserNode>" elements will append their content to DocBrowserNode parentDocBrowserNode; try { parentDocBrowserNode = m_docBrowserNodes[nodeId]; } catch (KeyNotFoundException) { throw new ProjectFileException(Resources.UndefinedNodeId, nodeId); } // set <docBrowser> to the DocBrowser that contains (or is) // <parentDocBrowserNode> DocBrowserNode docBrowserNode = parentDocBrowserNode; while (docBrowserNode.ParentNode != null) docBrowserNode = docBrowserNode.ParentNode; DocBrowser docBrowser = (DocBrowser) docBrowserNode; // parse the contents of the "<DocBrowserStaticPages>" element, if any if (!xmlReader.IsEmptyElement) { int initialDepth = xmlReader.Depth; while (xmlReader.Read() && (xmlReader.Depth != initialDepth)) { if (xmlReader.NodeType == XmlNodeType.Element) { if (xmlReader.Name == "DocBrowserNode") { ProcessDocBrowserNode(xmlReader, docBrowser, parentDocBrowserNode); } else { throw new ProjectFileException( Resources.ProjectFileUnexpectedElement, xmlReader.Name); } } else if (xmlReader.NodeType == XmlNodeType.Text) throw UnexpectedText(xmlReader); } } }
App.ProcessDocBrowserNode Method
Adds a node to a DocBrowser tree.
Parameters
xmlReader An XmlReader which, on entry, is positioned on a "<DocBrowserNode>" or "<SourceFilesNode>" element within the CodeDoc XML project file. On exit, xmlReader must be positioned on the "</DocBrowserNode>" or "</SourceFilesNode>" element. (or, equivalently, "<DocBrowserNode/> or "<SourceFilesNode/>"). docBrowser The current DocBrowser. parentDocBrowserNode The DocBrowserNode that the content specified by this "<DocBrowserNode>" element will be placed under.
Return Value
The newly-created |
DocBrowserNode ProcessDocBrowserNode(XmlReader xmlReader,
DocBrowser docBrowser, DocBrowserNode parentDocBrowserNode)
{
// parse attributes on the "<DocBrowserNode>" or "<SourceFilesNode>"
// element
string nodeId = GetAttribute(xmlReader, "NodeId", null);
bool unused;
string originalFilePath = MakePath(GetAttribute(xmlReader, "Path"),
FileFlags.File, out unused);
string fileName = GetAttribute(xmlReader, "NewFileName",
Path.GetFileName(originalFilePath));
string title = GetAttribute(xmlReader, "Title",
Path.GetFileNameWithoutExtension(fileName));
bool initiallyExpanded = GetAttribute<bool>(xmlReader,
"InitiallyExpanded", false);
// don't allow initially expanding a node if its parent isn't initially
// expanded
if (!parentDocBrowserNode.InitiallyExpanded)
initiallyExpanded = false;
// copy the original file into the DocBrowser pages directory
string newFilePath = Path.Combine(docBrowser.PagesAbsolutePath,
fileName);
try
{
CopyFileContents(originalFilePath, newFilePath, false);
}
catch (Exception)
{
// give the user extra information, i.e. the line number within
// the CodeDoc XML project file of the "<DocBrowserNode>" or
// "<SourceFilesNode>" element, before re-throwing the exception
Console.WriteLine(Resources.CopyFileError, m_projectFilePath,
((IXmlLineInfo)xmlReader).LineNumber);
throw;
}
// set <docBrowserNode> to a new DocBrowserNode object correpsonding
// to this XML node
DocBrowserNode docBrowserNode = new DocBrowserNode(nodeId,
m_docSet.GenerateTopicId(), originalFilePath, fileName, fileName,
title, initiallyExpanded);
// add <docBrowserNode> as a child of <parentDocBrowserNode>
parentDocBrowserNode.AddChild(docBrowserNode);
// add <docBrowserNode> to <m_docBrowserNodes> if the "NodeId"
// attribute was specified
if (nodeId != null)
m_docBrowserNodes[nodeId] = docBrowserNode;
// parse the contents of the "<DocBrowserNode>" or "<SourceFilesNode>"
// element, if any
if (!xmlReader.IsEmptyElement)
{
int initialDepth = xmlReader.Depth;
while (xmlReader.Read() && (xmlReader.Depth != initialDepth))
{
if (xmlReader.NodeType == XmlNodeType.Element)
{
if (xmlReader.Name == "IndexEntry")
{
// add an index entry
ProcessDocBrowserIndexEntry(xmlReader, docBrowserNode,
docBrowser);
}
else
if (xmlReader.Name == "DocBrowserNode")
{
// recursively add DocBrowser nodes
ProcessDocBrowserNode(xmlReader, docBrowser,
docBrowserNode);
}
else
{
throw new ProjectFileException(
Resources.ProjectFileUnexpectedElement,
xmlReader.Name);
}
}
else
if (xmlReader.NodeType == XmlNodeType.Text)
throw UnexpectedText(xmlReader);
}
}
return docBrowserNode;
}
App.ProcessDocBrowserIndexEntry Method
Adds an index entry to a DocBrowser tree, corresponding to a given node in the tree.
Parameters
xmlReader An XmlReader which, on entry, is positioned on a "<IndexEntry>" element within the CodeDoc XML project file. On exit, xmlReader must be positioned on the "</IndexEntry>" element (or, equivalently, "<IndexEntry/>"). docBrowserNode The DocBrowser node that contains the index entry. docBrowser The DocBrowser that contains the index. |
void ProcessDocBrowserIndexEntry(XmlReader xmlReader, DocBrowserNode docBrowserNode, DocBrowser docBrowser) { // parse attributes on the "<IndexEntry>" element string bookmark = GetAttribute(xmlReader, "Bookmark", null); // parse the contents of the "<IndexEntry>" element, if any if (!xmlReader.IsEmptyElement) { int initialDepth = xmlReader.Depth; while (xmlReader.Read() && (xmlReader.Depth != initialDepth)) { if (xmlReader.NodeType == XmlNodeType.Element) { throw new ProjectFileException( Resources.ProjectFileUnexpectedElement, xmlReader.Name); } else if (xmlReader.NodeType == XmlNodeType.Text) { docBrowser.AddIndexEntry(xmlReader.Value, docBrowserNode, bookmark); } } } }
App.ReadSource Method
Executes an "<AddSource>" CodeDoc XML project file command: adds a given source file, or all source files in a given directory tree, to the documentation set.
Parameters
xmlReader An XmlReader which, on entry, is positioned on a "<ReadSource>" element within the CodeDoc XML project file. On exit, xmlReader must be positioned on the "</ReadSource>" element (or, equivalently, "<ReadSource/>"). |
void ReadSource(XmlReader xmlReader) { // set <sourcePath> to the full path to the source file or directory string sourcePath = GetPath(xmlReader, "Path", m_projectFilePath); // set <prefix> to the value of the "Prefix" attribute; this is used // in the table of contents as the prefix for the relative file // path(s); use null if none string prefix = GetAttribute(xmlReader, "Prefix", null); // parse the contents of the "<ReadSource>" element, if any; set // <excludeFilePaths> to the collection of regular expressions // specified in "<ExcludeFilePaths>" elements; set <assemblyName> // to the "Name" attribute specified in an "<Assembly>" element, or // null if none List<Regex> excludeFilePaths = new List<Regex>(10); string assemblyName = null; if (!xmlReader.IsEmptyElement) { int initialDepth = xmlReader.Depth; while (xmlReader.Read() && (xmlReader.Depth != initialDepth)) { if (xmlReader.NodeType == XmlNodeType.Element) { if (xmlReader.Name == "ExcludeFilePaths") { excludeFilePaths.Add( GetRegexAttribute(xmlReader, "Regex")); } else if (xmlReader.Name == "Assembly") { assemblyName = GetAttribute(xmlReader, "Name"); } else { throw new ProjectFileException( Resources.ProjectFileUnexpectedElement, xmlReader.Name); } } else if (xmlReader.NodeType == XmlNodeType.Text) throw UnexpectedText(xmlReader); } } // add <sourcePath> to the documentation set bool isDir; string sourceFileOrDirPath = MakePath(sourcePath, FileFlags.File | FileFlags.Directory, out isDir); if (isDir) { m_docSet.AddSourceFilesInDirectoryTree(sourceFileOrDirPath, (prefix ?? ""), excludeFilePaths, assemblyName); } else { if (!MatchRegexList(excludeFilePaths, sourceFileOrDirPath, false)) { try { string relativePath = Path.GetFileName(sourceFileOrDirPath); if (prefix != null) relativePath = Path.Combine(prefix, relativePath); m_docSet.AddSourceFile(sourceFileOrDirPath, relativePath, assemblyName); } catch (ArgumentException ex) { // unsupported source file name extension throw new ProjectFileException("{0}", ex.Message); } } } }
App.WriteDocumentation Method
Executes a "<WriteDocumentation>" CodeDoc XML project file command: generates documentation from the source files in the documentation set.
Parameters
xmlReader An XmlReader which, on entry, is positioned on a "<WriteDocumentation>" element within the CodeDoc XML project file. On exit, xmlReader must be positioned on the "</WriteDocumentation>" element (or, equivalently, "<WriteDocumentation/>"). |
void WriteDocumentation(XmlReader xmlReader) { // reset the state of <n>m_docSet</n> to be the same as it was before // the last call to WriteDocumentation() or WriteFormattedCode(). ResetDocSet(); // set <docHtmlDirPath> to the full path to the documentation HTML // directory; create it if it doesn't exist bool unused; string docHtmlDirPath = MakePath(GetPath(xmlReader, "Path", m_projectFilePath), FileFlags.Directory | FileFlags.Nonexistent, out unused); CreateDirectoryIfNeeded(docHtmlDirPath); // parse other attributes on the "<WriteDocumentation>" element m_docSet.ExternalOnly = GetAttribute<bool>(xmlReader, "ExternalOnly", false); bool markOfTheWeb = GetAttribute<bool>(xmlReader, "MarkOfTheWeb", true); string topicFooterHtml = GetAttribute(xmlReader, "TopicFooterHtml", null); // parse the contents of the "<WriteDocumentation>" element, if any; // set <namespaceRegexList> to the collection of regular expressions // specified in "<Namespace>" elements -- these regular expressions // identify which namespaces to include in the documentation set; // if none are specified, all namespaces are included; similarly, // <memberRegexList> lists member names to include (unless the list // is empty, in which case all members are included); set // <hideAttributesRegexList> to the list of regular expressions which // match "[attribute]" declarations we don't want to display in the // generated documentation List<Regex> namespaceRegexList = new List<Regex>(10); List<Regex> memberRegexList = new List<Regex>(10); List<Regex> hideAttributesRegexList = new List<Regex>(10); TopicRenderer topicRenderer = null; DocBrowserNode addToDocBrowserNode = null; InitiallyExpand initiallyExpand = InitiallyExpand.None; string indexLabelSuffix = null; List<KeyValuePair<Regex, string>> namespaceTitleReplacements = null; if (!xmlReader.IsEmptyElement) { int initialDepth = xmlReader.Depth; while (xmlReader.Read() && (xmlReader.Depth != initialDepth)) { if (xmlReader.NodeType == XmlNodeType.Element) { if (xmlReader.Name == "HideAttributes") { hideAttributesRegexList.Add( GetRegexAttribute(xmlReader, "Regex")); } else if (xmlReader.Name == "IncludeNamespaces") { namespaceRegexList.Add( GetRegexAttribute(xmlReader, "Regex")); } else if (xmlReader.Name == "IncludeMembers") { memberRegexList.Add( GetRegexAttribute(xmlReader, "Regex")); } else if (xmlReader.Name == "CopyFile") { string sourcePath = GetPath(xmlReader, "Path", m_projectFilePath); string destinationFileName = GetAttribute(xmlReader, "NewName", Path.GetFileName(sourcePath)); CopyStaticFile(sourcePath, destinationFileName, docHtmlDirPath, xmlReader); } else if (xmlReader.Name == "AddToDocBrowser") { string nodeId = GetAttribute(xmlReader, "NodeId"); initiallyExpand = GetInitiallyExpandAttribute(xmlReader, "InitiallyExpand"); indexLabelSuffix = GetAttribute(xmlReader, "IndexLabelSuffix", null); try { addToDocBrowserNode = m_docBrowserNodes[nodeId]; } catch (KeyNotFoundException) { throw new ProjectFileException( Resources.UndefinedNodeId, nodeId); } namespaceTitleReplacements = ReadChangeTitleElements(xmlReader); } else if (xmlReader.Name == "TopicRenderer") { try { topicRenderer = TopicRenderer.CreateRenderer( GetAttribute(xmlReader, "Assembly"), GetAttribute(xmlReader, "Type"), markOfTheWeb, topicFooterHtml, hideAttributesRegexList); } catch (CreateExternalObjectException ex) { throw new ProjectFileException("{0}", ex.Message); } } else { throw new ProjectFileException( Resources.ProjectFileUnexpectedElement, xmlReader.Name); } } else if (xmlReader.NodeType == XmlNodeType.Text) throw UnexpectedText(xmlReader); } } // if no "<TopicRenderer>" element was specified, create an instance of // the default topic renderer if (topicRenderer == null) { topicRenderer = TopicRenderer.CreateRenderer("CodeDocRenderer", "DwellNet.CodeDoc.DefaultTopicRenderer", markOfTheWeb, topicFooterHtml, hideAttributesRegexList); } // receive events from <topicRenderer> topicRenderer.WarningInSourceFile += new WarningInSourceFileEventDelegate( topicRenderer_WarningInSourceFile); // perform initial processing on topics topicRenderer.CodeHtml = false; ProcessTopics(namespaceRegexList, memberRegexList, topicRenderer); // loop once for each topic in the documentation set Console.WriteLine(Resources.WritingTopics, m_documentedTopicCount); foreach (Topic topic in m_docSet.Topics) { // skip undocumented topics if (!topic.IsDocumented) continue; // generate a documentation HTML file for each documented topic try { TopicRenderer.GenerateDocHtmlInDirectory(topic, docHtmlDirPath, topicRenderer); } catch (ParsingException ex) { Console.Error.WriteLine(ex.WarningMessage); m_warningCount++; } } // if specified, add documented topics to a specified DocBrowser if (addToDocBrowserNode != null) { AddTopicsToDocBrowserContents(initiallyExpand, namespaceTitleReplacements, indexLabelSuffix, addToDocBrowserNode, false); } }
App.WriteFormattedCode Method
Executes a "<WriteFormattedCode>" CodeDoc XML project file command: generates code HTML from the source files in the documentation set.
Parameters
xmlReader An XmlReader which, on entry, is positioned on a "<WriteFormattedCode>" element within the CodeDoc XML project file. On exit, xmlReader must be positioned on the "</WriteFormattedCode>" element (or, equivalently, "<WriteFormattedCode/>"). |
void WriteFormattedCode(XmlReader xmlReader) { // reset the state of <n>m_docSet</n> to be the same as it was before // the last call to WriteDocumentation() or WriteFormattedCode(). ResetDocSet(); // set <codeHtmlDirPath> to the full path to the code HTML directory; // create it if it doesn't exist bool unused; string codeHtmlDirPath = MakePath(GetPath(xmlReader, "Path", m_projectFilePath), FileFlags.Directory | FileFlags.Nonexistent, out unused); CreateDirectoryIfNeeded(codeHtmlDirPath); // parse other attributes on the "<WriteFormattedCode>" element InitiallyExpand initiallyExpand = InitiallyExpand.None; bool markOfTheWeb = GetAttribute<bool>(xmlReader, "MarkOfTheWeb", true); string topicFooterHtml = GetAttribute(xmlReader, "TopicFooterHtml", null); // parse the contents of the "<WriteFormattedCode>" element, if any SourceFileRenderer sourceFileRenderer = null; TopicRenderer topicRenderer = null; DocBrowserNode addToDocBrowserNode = null; DocBrowserNode sourceFilesDocBrowserNode = null; string indexLabelSuffix = null; List<KeyValuePair<Regex, string>> namespaceTitleReplacements = null; if (!xmlReader.IsEmptyElement) { int initialDepth = xmlReader.Depth; while (xmlReader.Read() && (xmlReader.Depth != initialDepth)) { if (xmlReader.NodeType == XmlNodeType.Element) { if (xmlReader.Name == "CopyFile") { string sourcePath = GetPath(xmlReader, "Path", m_projectFilePath); string destinationFileName = GetAttribute(xmlReader, "NewName", Path.GetFileName(sourcePath)); CopyStaticFile(sourcePath, destinationFileName, codeHtmlDirPath, xmlReader); } else if (xmlReader.Name == "AddToDocBrowser") { string nodeId = GetAttribute(xmlReader, "NodeId"); initiallyExpand = GetInitiallyExpandAttribute(xmlReader, "InitiallyExpand"); indexLabelSuffix = GetAttribute(xmlReader, "IndexLabelSuffix", null); try { addToDocBrowserNode = m_docBrowserNodes[nodeId]; } catch (KeyNotFoundException) { throw new ProjectFileException( Resources.UndefinedNodeId, nodeId); } namespaceTitleReplacements = ReadChangeTitleElements(xmlReader); } else if (xmlReader.Name == "SourceFilesNode") { if (addToDocBrowserNode == null) { throw new ProjectFileException( Resources.SourceFilesNodeUsesAddToDocBrowser); } sourceFilesDocBrowserNode = ProcessDocBrowserNode( xmlReader, addToDocBrowserNode.DocBrowser, addToDocBrowserNode); } else if (xmlReader.Name == "SourceFileRenderer") { try { sourceFileRenderer = SourceFileRenderer.CreateRenderer( GetAttribute(xmlReader, "Assembly"), GetAttribute(xmlReader, "Type"), markOfTheWeb); } catch (CreateExternalObjectException ex) { throw new ProjectFileException("{0}", ex.Message); } } else if (xmlReader.Name == "TopicRenderer") { try { topicRenderer = TopicRenderer.CreateRenderer( GetAttribute(xmlReader, "Assembly"), GetAttribute(xmlReader, "Type"), markOfTheWeb, topicFooterHtml, null); } catch (CreateExternalObjectException ex) { throw new ProjectFileException("{0}", ex.Message); } } else { throw new ProjectFileException( Resources.ProjectFileUnexpectedElement, xmlReader.Name); } } else if (xmlReader.NodeType == XmlNodeType.Text) throw UnexpectedText(xmlReader); } } // if no "<SourceFileRenderer>" element was specified, create an // instance of the default source file renderer if (sourceFileRenderer == null) { sourceFileRenderer = SourceFileRenderer.CreateRenderer( "CodeDocRenderer", "DwellNet.CodeDoc.DefaultSourceFileRenderer", markOfTheWeb); } // if no "<TopicRenderer>" element was specified, create an instance // of the default topic renderer if (topicRenderer == null) { topicRenderer = TopicRenderer.CreateRenderer("CodeDocRenderer", "DwellNet.CodeDoc.DefaultTopicRenderer", markOfTheWeb, topicFooterHtml, null); } // perform initial processing on topics topicRenderer.CodeHtml = true; ProcessTopics(null, null, topicRenderer); // write formatted source code HTML for each source file in the // documentation set Console.WriteLine(Resources.WritingFormattedSourceFiles, m_docSet.SourceFiles.Count); foreach (SourceFile sourceFile in m_docSet.SourceFiles) { string htmlPath = Path.Combine(codeHtmlDirPath, sourceFile.CodeHtmlFileName); sourceFileRenderer.SourceFile = sourceFile; sourceFileRenderer.GenerateCodeHtml(topicRenderer, htmlPath); } // generate code namespace files foreach (Topic topic in m_docSet.NamespaceTopics) { try { string htmlPath = Path.Combine(codeHtmlDirPath, topic.CodeNamespaceHtmlFileName); using (StreamWriter streamWriter = new StreamWriter(htmlPath)) { using (HtmlTextWriterHelper htmlWriter = new HtmlTextWriterHelper(streamWriter)) { topicRenderer.Topic = topic; topicRenderer.GenerateCodeNamespaceHtml(htmlWriter); } } } catch (ParsingException ex) { Console.Error.WriteLine(ex.WarningMessage); m_warningCount++; } } // generate code overload topic files foreach (Topic topic in m_docSet.OverloadListTopics) { try { string htmlPath = Path.Combine(codeHtmlDirPath, topic.CodeOverloadListHtmlFileName); using (StreamWriter streamWriter = new StreamWriter(htmlPath)) { using (HtmlTextWriterHelper htmlWriter = new HtmlTextWriterHelper(streamWriter)) { topicRenderer.Topic = topic; topicRenderer.GenerateCodeOverloadListHtml( htmlWriter); } } } catch (ParsingException ex) { Console.Error.WriteLine(ex.WarningMessage); m_warningCount++; } } // if specified (by an "<AddToDocBrowser>" element in the CodeDoc XML // project file), add documented topics to the specified DocBrowser if (addToDocBrowserNode != null) { // write DocBrowser Contents nodes corresponding to namespaces, // types and members AddTopicsToDocBrowserContents(initiallyExpand, namespaceTitleReplacements, indexLabelSuffix, addToDocBrowserNode, true); // if specified (by a "<SourceFilesNode>" element in the CodeDoc // XML project file), write DocBrowser Contents nodes corresponding // to source files if (sourceFilesDocBrowserNode != null) { DocBrowser docBrowser = sourceFilesDocBrowserNode.DocBrowser; foreach (SourceFile sourceFile in m_docSet.SourceFiles) { // add a node to the table of contents string url = sourceFile.CodeHtmlFileName; DocBrowserNode node = new DocBrowserNode(null, sourceFile.SourceFileTopicId, null, url, url, sourceFile.SourceFileRelativePath, false); sourceFilesDocBrowserNode.AddChild(node); // add an index entry (e.g. "Foo.cs (Source Code)"); if the // source file relative path includes a directory component // (e.g. "Abc\Def\Foo.cs") include an entry for both the // full path (e.g. "Abc\Def\Foo.cs") and base file name // (e.g. // "Foo.cs") string fileName1 = sourceFile.SourceFileRelativePath; string fileName2 = Path.GetFileName(fileName1); docBrowser.AddIndexEntry(fileName1 + indexLabelSuffix, node, null); if (fileName1 != fileName2) { docBrowser.AddIndexEntry(fileName2 + indexLabelSuffix, node, null); } } } } }
App.ReadChangeTitleElements Method
Parses "<ChangeTitle>" elements.
Parameters
xmlReader An XmlReader pointing to the current location within the CodeDoc XML project file.
Return Value
A list of change-title Regex/replacement-string pairs, or null if no "<ChangeTitle>" elements were present. |
List<KeyValuePair<Regex, string>> ReadChangeTitleElements( XmlReader xmlReader) { List<KeyValuePair<Regex, string>> titleReplacements = null; int initialDepth = xmlReader.Depth; while (xmlReader.Read() && (xmlReader.Depth != initialDepth)) { if (xmlReader.NodeType == XmlNodeType.Element) { if (xmlReader.Name == "ChangeTitle") { if (titleReplacements == null) { titleReplacements = new List<KeyValuePair<Regex, string>>(); } Regex regex = GetRegexAttribute(xmlReader, "Regex"); string replacement = GetAttribute(xmlReader, "Replacement"); titleReplacements.Add(new KeyValuePair<Regex, string> (regex, replacement)); } else { throw new ProjectFileException( Resources.ProjectFileUnexpectedElement, xmlReader.Name); } } else if (xmlReader.NodeType == XmlNodeType.Text) throw UnexpectedText(xmlReader); } return titleReplacements; }
App.ResetDocSet Method
Resets the state of m_docSet to be the same as it was before the last call to WriteDocumentation() or WriteFormattedCode(). |
void ResetDocSet() { // if ProcessTopics() was previously called, we need to reset the state // of <m_docSet> if (m_processedTopics) { // create a new DocumentationSet DocumentationSet newDocSet = new DocumentationSet(); // copy "core" state from the old DocumentationSet to the new one m_docSet.CopyCoreState(newDocSet); // switch <m_docSet> over to the new DocumentationSet m_docSet = newDocSet; // initialize <m_docSet> InitializeDocumentationSet(); } else m_processedTopics = true; // other initialization m_documentedTopicCount = 0; }
App.ProcessTopics Method
Performs initial processing on topics that's common to WriteDocumentation and WriteFormattedCode.
Parameters
namespaceRegexList The collection of regular expressions specified in "<Namespace>" elements -- these regular expressions identify which namespaces to include in the documentation set; if none are specified, or namespaceRegexList is null, all namespaces are included. memberRegexList The collection of regular expressions specified in "<Member>" elements -- these regular expressions identify the member names names to include in the documentation set; if none are specified, or memberRegexList is null, all members are included. topicRenderer The TopicRenderer to use to generate HTML corresponding to XML comments. |
void ProcessTopics(List<Regex> namespaceRegexList, List<Regex> memberRegexList, TopicRenderer topicRenderer) { // perform general processing on the list of topics, such as generating // unique file names; do this before any topic lists get sorted so that // the file name suffix numbering is relatively consistent among runs m_docSet.BeginUsingSources(); // for debugging purposes, list all topics if (m_verbose >= 99) { foreach (Topic topic in m_docSet.Topics) Console.WriteLine(topic.QualifiedMemberName); } // pass 1: sets Topic.IsDocumented to true for each topic we want to // generate documentation for; also, update <m_documentedTopicCount> to // be the count of documented topics Console.WriteLine(Resources.EnumeratingTopicsToDocument); foreach (NamespaceTopic namespaceTopic in m_docSet.NamespaceTopics) { // see if <namespaceRegexList> matches this namespace; if the // list is empty, all namespaces are included if (!MatchRegexList(namespaceRegexList, namespaceTopic.MemberName, true)) continue; // keep track of the number of topics that will be documented in // this namespace, not including the namespace itself int documentedTopicsInNamespace = 0; // sort topics namespaceTopic.SortTypeTopics(); namespaceTopic.SortChildTopics(); // loop once per type in this namespace foreach (Topic typeTopic in namespaceTopic.TypeTopics) { // mark <typeTopic> as documentable if <memberRegexList> // matches it or is empty if (MatchRegexList(memberRegexList, typeTopic.QualifiedMemberName.ToString(), true)) { if (!m_docSet.ExternalOnly || typeTopic.IsExternallyAccessible) { typeTopic.IsDocumented = true; documentedTopicsInNamespace++; m_documentedTopicCount++; } } // loop once per member of type <typeTopic>, but skip nested // type topics since we're already enumerating through all type // topics foreach (Topic childTopic in Topic.EnumerateDescendentNonTypes(typeTopic)) { // mark <childTopic> as documentable if <memberRegexList> // matches it or is empty; exclude enumeration value topics // since we only document the enumeration topics (not // one topic for each individual value) if (MatchRegexList(memberRegexList, childTopic.QualifiedMemberName.ToString(), true) && (childTopic.TopicKind != TopicKind.EnumValueTopic) && (!m_docSet.ExternalOnly || childTopic.IsExternallyAccessible)) { childTopic.IsDocumented = true; documentedTopicsInNamespace++; m_documentedTopicCount++; } } } // mark this namespace topic as documentable, unless it has no // child topics if (documentedTopicsInNamespace > 0) { namespaceTopic.IsDocumented = true; m_documentedTopicCount++; } } // perform general processing on the list of topics, such as // determining the base class of each documented topic, that must occur // after the application has set Topic.IsDocumented to true for all // topics the applicatin wants documented m_docSet.AfterSettingIsDocumented(); // pass 2: generates Topic.XmlCommentSections Console.WriteLine(Resources.ConvertingXmlToHtml); foreach (Topic topic in m_docSet.Topics) { if (topic.CanLinkTo) { try { TopicRenderer.GenerateDocHtmlInDirectory(topic, null, topicRenderer); } catch (ParsingException ex) { Console.Error.WriteLine(ex.WarningMessage); m_warningCount++; } } } }
App.CopyStaticFile Method
Copies a static file (i.e. a file that's not generated) to the documentation |