/*       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;

    
App.m_processedTopics Field

true once ProcessTopics was called.

    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.

    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 HTML directory or code HTML directory.

Parameters

sourcePath

The path of the file to copy.

destinationFileName

The file name (without a path) to use for the file copy. If null, the file name portion of sourcePath is used.

destinationDirPath

The full path to the destination HTML directory.

xmlReader

An XmlReader pointing to the current location within the CodeDoc XML project file.

    void CopyStaticFile(string sourcePath, string destinationFileName,
        string destinationDirPath, XmlReader xmlReader)
    {
        if (destinationFileName == null)
            destinationFileName = Path.GetFileName(sourcePath);
        string destinationPath = Path.Combine(destinationDirPath,
            destinationFileName);
        bool textToHtml = GetAttribute<bool>(xmlReader, "TextToHtml", false);
        try
        {
            if (textToHtml)
                ConvertTextFileToHtml(sourcePath, destinationPath, true);
            else
                CopyFileContents(sourcePath, destinationPath, true);
        }
        catch (Exception)
        {
            Console.WriteLine(Resources.CopyFileError, m_projectFilePath,
                ((IXmlLineInfo)xmlReader).LineNumber);
            throw;
        }
    }

#if false
    /// <summary>
    /// Writes the BasicContents.htm file.
    /// </summary>
    ///
    /// <param name="htmlWriter">Open onto the BasicContents.htm file.</param>
    ///
    /// <param name="markOfTheWeb"><n>true</n> to include the Mark of the Web
    ///     on the page.  See <r>HtmlTextWriterHelper.BeginHtmlDocument</r>
    ///     for more information.</param>
    ///
    void WriteBasicContentsHtm(HtmlTextWriterHelper htmlWriter,
        bool markOfTheWeb)
    {
        // begin the HTML document
        htmlWriter.BeginHtmlDocument(HtmlDocType.XhtmlTransitional,
            markOfTheWeb);

        // begin the "<head>" block
        htmlWriter.FormatBeginTag("head\n");

        // write the "<title>" block
        htmlWriter.RenderBeginTag("title");
        htmlWriter.WriteEncodedText(Resources.DefaultContentsTitle);
        htmlWriter.FormatEndTag("\n\n");

        // write the "<link>" element
        htmlWriter.FormatTag("link,rel,href\n", "stylesheet", "_CodeDoc.css");

        // end the "<head>" block
        htmlWriter.FormatEndTag("\n\n");

        // begin the "<body>" block
        htmlWriter.FormatBeginTag("body\n");

        // write the body text -- loop once for each namespace
        List<Topic> childTopics = new List<Topic>(100);
        foreach (NamespaceTopic namespaceTopic in
            m_docSet.GetSortedNamespaceTopics())
        {
            // skip this namespace if it's not documented
            if (!namespaceTopic.IsDocumented)
                continue;

            // write the namespace header tag
            htmlWriter.FormatBeginTag("p,class", "ContentsNamespace");
            htmlWriter.FormatBeginTag("a,target,href,title", "CodeDocTopic",
                namespaceTopic.DocHtmlFileName, namespaceTopic.LongTitle);
            htmlWriter.WriteEncodedText(namespaceTopic.LongTitle);
            htmlWriter.RenderEndTag();
            htmlWriter.FormatEndTag("\n");

            // loop once for each type within the namespace
            foreach (Topic typeTopic in namespaceTopic.TypeTopics)
            {
                // skip this type if it's not documented
                if (!typeTopic.IsDocumented)
                    continue;

                // skip this topic if it an overload list topic
                if (typeTopic is OverloadListTopic)
                    continue;

                // write the type header tag
                htmlWriter.FormatBeginTag("p,class", "ContentsType");
                htmlWriter.FormatBeginTag("a,target,href,title",
                    "CodeDocTopic", typeTopic.DocHtmlFileName,
                    typeTopic.LongTitle);
                htmlWriter.WriteEncodedText(typeTopic.ShortTitle);
                htmlWriter.RenderEndTag();
                htmlWriter.FormatEndTag("\n");

                // loop once for each member of this type topic, but skip type
                // topics since we're already enumerating through all type
                // topics
                childTopics.Clear();
                foreach (Topic childTopic in
                        Topic.EnumerateDescendentNonTypes(typeTopic))
                    childTopics.Add(childTopic);
                childTopics.Sort();
                foreach (Topic childTopic in childTopics)
                {
                    // skip this topic if it's not documented
                    if (!childTopic.IsDocumented)
                        continue;

                    // skip this topic if it's included in an overload list
                    // topic
                    if (childTopic.OverloadListTopic != null)
                        continue;

                    // write the link to the child topic
                    htmlWriter.FormatBeginTag("p,class", "ContentsMember");
                    htmlWriter.FormatBeginTag("a,target,href,title",
                        "CodeDocTopic", childTopic.DocHtmlFileName,
                        childTopic.LongTitle);
                    htmlWriter.WriteEncodedText(childTopic.VeryShortTitle);
                    htmlWriter.RenderEndTag();
                    htmlWriter.FormatEndTag("\n");
                }
            }
        }

        // write the footer
        htmlWriter.FormatBeginTag("div,class", "ContentsFooter");
        htmlWriter.FormatEndTag("\n\n");

        // end the "<body>" block
        htmlWriter.FormatEndTag("\n");

        // end the HTML document
        htmlWriter.EndHtmlDocument();
    }
#endif

    
App.WriteDocBrowserFrameset Method

Writes the root frameset HTML file of the DocBrowser web UI.

Parameters

docBrowser

The DocBrowser to generate the root frameset HTML file for.

basic

If true, write HTML for the basic (downlevel) version of the DocBrowser. Otherwise write HTML for the regular (DHTML) version.

htmlWriter

Open onto the DocBrowser root frameset HTML file.

    void WriteDocBrowserFrameset(DocBrowser docBrowser, bool basic,
        HtmlTextWriterHelper htmlWriter)
    {
        // set <firstNode> to the topmost node to display (null if none)
        ICollection<DocBrowserNode> childNodes = docBrowser.ChildNodes;
        DocBrowserNode firstNode;
        if (childNodes != null)
        {
            IEnumerator<DocBrowserNode> nodeEnumerator =
                docBrowser.ChildNodes.GetEnumerator();
            if (nodeEnumerator.MoveNext())
                firstNode = nodeEnumerator.Current;
            else
                firstNode = null;
        }
        else
            firstNode = null;

        // set <scriptPath>, <tabsPath>, <contentsPath>, <indexPath>, and
        // <firstTopicPath> to the path to the script file and tabs, contents,
        // index and initial topic frame pages, relative to the location of the
        // root frameset pages
        string scriptPath, tabsPath, contentsPath, indexPath, firstTopicPath;
        if (!basic)
        {
            scriptPath = "_DocBrowser.js";
            tabsPath = docBrowser.TabsHtmlFileName;
            contentsPath = docBrowser.ContentsHtmlFileName;
            indexPath = docBrowser.IndexHtmlFileName;
            firstTopicPath = (firstNode != null) ? firstNode.Url : null;
        }
        else
        {
            scriptPath = null; // unused in basic DocBrowser
            tabsPath = null; // unused in basic DocBrowser
            contentsPath = Resources.BasicDocBrowserPrefix +
                docBrowser.ContentsHtmlFileName;
            indexPath = Resources.BasicDocBrowserPrefix +
                docBrowser.IndexHtmlFileName;
            if (firstNode != null)
                firstTopicPath = firstNode.Url;
            else
                firstTopicPath = null;
        }
        if (docBrowser.PagesRelativePath != null)
        {
            if (scriptPath != null)
            {
                scriptPath = Path.Combine(docBrowser.PagesRelativePath,
                    scriptPath);
            }
            if (tabsPath != null)
            {
                tabsPath = Path.Combine(docBrowser.PagesRelativePath,
                    tabsPath);
            }
            contentsPath = Path.Combine(docBrowser.PagesRelativePath,
                contentsPath);
            indexPath = Path.Combine(docBrowser.PagesRelativePath, indexPath);
            if (firstTopicPath != null)
            {
                firstTopicPath = Path.Combine(docBrowser.PagesRelativePath,
                    firstTopicPath);
            }
        }

        // switch paths to URL form
        if (scriptPath != null)
            scriptPath = scriptPath.Replace('\\', '/');
        if (tabsPath != null)
            tabsPath = tabsPath.Replace('\\', '/');
        contentsPath = contentsPath.Replace('\\', '/');
        indexPath = indexPath.Replace('\\', '/');
        if (firstTopicPath != null)
            firstTopicPath = firstTopicPath.Replace('\\', '/');

        // begin the HTML document
        htmlWriter.BeginHtmlDocument(HtmlDocType.XhtmlFrameset,
            docBrowser.MarkOfTheWeb);

        // begin the "<head>" block
        htmlWriter.FormatBeginTag("head\n");

        // write the "<title>" block if specified
        if (docBrowser.Title != null)
        {
            htmlWriter.RenderBeginTag("title");
            htmlWriter.WriteEncodedText(docBrowser.Title);
            htmlWriter.FormatEndTag("\n\n");
        }

        // write the "<script>" block if applicable
        if (!basic)
        {
            htmlWriter.FormatBeginTag("script,src,type", scriptPath,
                "text/javascript");
            htmlWriter.FormatEndTag("\n");
        }

        // end the "<head>" block
        htmlWriter.FormatEndTag("\n\n");

        // begin the outer frameset
        htmlWriter.FormatBeginTag("frameset,cols\n",
            String.Format("{0},*", docBrowser.LeftFrameWidth));

        // write the contents of the outer frameset
        if (!basic)
        {
            // regular (DHTML) version
            htmlWriter.FormatBeginTag("frameset,id,rows,frameBorder,frameSpacing,tabIndex",
                "LeftFrameset", "23,*,0", "0", "0", "-1");
            htmlWriter.FormatTag("frame,id,src,scrolling,tabIndex\n",
                "TabsFrame", tabsPath, "no", "0");
            htmlWriter.FormatTag("frame,id,src,tabIndex\n",
                "ContentsFrame", contentsPath, "0");
            htmlWriter.FormatTag("frame,id,src,tabIndex\n",
                "IndexFrame", indexPath, "0");
            htmlWriter.FormatEndTag("\n"); // "frameset"
            htmlWriter.FormatTag("frame,id,src,tabIndex,onLoad\n",
                "DocTopic", ""/*firstTopicPath ?? String.Empty*/, "1",
                "DB_DocTopic_OnLoad()");
        }
        else
        {
            // basic (downlevel) version
            htmlWriter.FormatBeginTag("frameset,rows", "*,*");
            htmlWriter.FormatTag("frame,src\n", contentsPath);
            htmlWriter.FormatTag("frame,src\n", indexPath);
            htmlWriter.FormatEndTag("\n"); // "frameset"
            htmlWriter.FormatTag("frame,name,src\n",
                "DocTopic", firstTopicPath ?? String.Empty);
        }

        // begin the outer frameset
        htmlWriter.FormatEndTag("\n"); // "frameset"

        // end the HTML document
        htmlWriter.EndHtmlDocument();
    }

    
App.WriteDocBrowserContentsHtml Method

Writes the DocBrowser *_Contents.htm file.

Parameters

docBrowser

The DocBrowser to generate Contents for.

basic

If true, write HTML for the basic (downlevel) version of the DocBrowser. Otherwise write HTML for the regular (DHTML) version.

htmlWriter

Open onto the DocBrowser *_Contents.htm file.

Return Value

The number of nodes in the DocBrowser Contents tree.

    int WriteDocBrowserContentsHtml(DocBrowser docBrowser, bool basic,
        HtmlTextWriterHelper htmlWriter)
    {
        // begin the HTML document
        htmlWriter.BeginHtmlDocument(HtmlDocType.XhtmlTransitional,
            docBrowser.MarkOfTheWeb);

        // begin the "<head>" block
        htmlWriter.FormatBeginTag("head\n");

        // write the stylesheet "<link>" block
        htmlWriter.FormatTag("link,rel,href\n\n", "stylesheet", "_CodeDoc.css");

        // write the "<script>" blocks if applicable
        if (!basic)
        {
            // write the "<script>" block that loads _DocBrowser.js
            htmlWriter.FormatBeginTag("script,src,type", "_DocBrowser.js",
                "text/javascript");
            htmlWriter.FormatEndTag("\n\n");

            // write the "<script>" block that redirects to "_SyncToc.htm" if this page is loaded
            // outside the context of a DocBrowser frameset
            htmlWriter.FormatBeginTag("script,type", "text/javascript");
            htmlWriter.WriteLine("if (parent.DocTopic == undefined)");
            htmlWriter.WriteLine("  window.navigate(\"_SyncToc.htm\");");
            htmlWriter.FormatEndTag("\n\n");

            // write the topic map script block -- this defines a script array
            // that maps a topic name (e.g. "Intro.htm") to a numeric topic ID
            // (e.g. 1000001) for those topics that don't call OnTopicLoaded()
            // (in _DocBrowser.js) when they load
            htmlWriter.FormatBeginTag("script,type", "text/javascript");
            htmlWriter.WriteLine("dbc_TopicMap = new Array;");
            foreach (DocBrowserNode node in docBrowser.AllNodes)
            {
                if (node.OriginalFilePath != null)
                {
                    htmlWriter.WriteLine("dbc_TopicMap[\"{0}\"] = {1};",
                        node.FileName, node.TopicId);
                }
            }
            htmlWriter.FormatEndTag("\n");
        }

        // end the "<head>" block
        htmlWriter.FormatEndTag("\n\n");

        // begin the "<body>" block
        if (!basic)
        {
            htmlWriter.FormatBeginTag(
                "body,id,onLoad,onSelectStart,onMouseDown,onMouseOver,onKeyDown,onKeyPress",
                "DocBrowserContents", "return DBC_OnLoad()", "return false",
                "return DBC_OnMouseDown()", "return DBC_OnMouseOver()",
                "return DBC_OnKeyDown()", "return DBC_OnKeyPress()");
        }
        else
            htmlWriter.FormatBeginTag("body,id", "BasicDocBrowserContents");

        // the basic version includes a header
        if (basic)
        {
            string pathToEnhancedFrameset = docBrowser.FramesetFileName;
            string pathFromPagesToRoot = docBrowser.PathFromPagesToRoot;
            if (pathFromPagesToRoot != null)
            {
                pathToEnhancedFrameset = Path.Combine(pathFromPagesToRoot,
                    pathToEnhancedFrameset);
            }
            htmlWriter.FormatBeginTag("table,class,cellpadding,cellspacing",
                "Header_", "0", "0");
            htmlWriter.RenderBeginTag("tr");
            htmlWriter.FormatBeginTag("td,class", "Left_");
            htmlWriter.WriteEncodedText(
                Resources.BasicDocBrowserContentsHeader);
            htmlWriter.FormatEndTag("\n"); // "td"
            htmlWriter.FormatBeginTag("td,class", "Right_");
#if false // omit "Use Enhanced View" link
            htmlWriter.FormatBeginTag("a,target,href,title", "_parent",
                pathToEnhancedFrameset,
                Resources.BasicDocBrowserUseEnhancedToolTip);
            htmlWriter.WriteEncodedText(Resources.BasicDocBrowserUseEnhanced);
            htmlWriter.RenderEndTag(); // "a"
#endif
            htmlWriter.FormatEndTag("\n"); // "td"
            htmlWriter.RenderEndTag(); // "tr"
            htmlWriter.FormatEndTag("\n"); // "table"
        }

        // generate HTML for the tree nodes; set <topicCount> to the number of
        // tree nodes generated
        int topicCount;
        ICollection<DocBrowserNode> childNodes = docBrowser.ChildNodes;
        if (childNodes != null)
        {
            topicCount = WriteDocBrowserNodes(childNodes, basic, 1,
                htmlWriter);
        }
        else
            topicCount = 0;

        // end the "<body>" block
        htmlWriter.FormatEndTag("\n\n");

        // end the HTML document
        htmlWriter.EndHtmlDocument();

        // done
        return topicCount;
    }

    
App.WriteDocBrowserNodes Method

Writes HTML for a collection of sibling DocBrowser nodes.

Parameters

nodes

The nodes to generate HTML for.

basic

If true, write HTML for the basic (downlevel) version of the DocBrowser. Otherwise write HTML for the regular (DHTML) version.

level

The level within the tree; 1 is the topmost level.

htmlWriter

Where to write the HTML.

    int WriteDocBrowserNodes(ICollection<DocBrowserNode> nodes, bool basic,
        int level, HtmlTextWriterHelper htmlWriter)
    {
        // loop once for each child <node> of <parentNode>
        int topicCount = 0;
        foreach (DocBrowserNode node in nodes)
        {
            // begin the "<div>" of this tree node
            if (!basic)
            {
                htmlWriter.FormatBeginTag("div,class,id,Topic",
                    String.Format("L{0}_", level),
                    String.Format("Topic{0}", node.TopicId),
                    node.Url);
            } 
            else
            {
                htmlWriter.FormatBeginTag("div,class",
                    String.Format("L{0}_", level));
            }

            // set <childNodes> to the collection of children of <node>
            ICollection<DocBrowserNode> childNodes = node.ChildNodes;

            if (!basic)
            {
                // write the icon for this tree node
                if (childNodes != null)
                {
                    if (node.InitiallyExpanded)
                        htmlWriter.FormatTag("img,src", "_Opened.gif");
                    else
                        htmlWriter.FormatTag("img,src", "_Closed.gif");
                }
                else
                    htmlWriter.FormatTag("img,src", "_Leaf.gif");

                // write the text contents of the tree node
                htmlWriter.RenderBeginTag("span");
                htmlWriter.WriteEncodedText(node.Title);
                htmlWriter.RenderEndTag(); // "span"
            }
            else
            {
                // write the hyperlink for this tree node
                htmlWriter.FormatBeginTag("a,target,href", "DocTopic",
                    node.Url);
                if (childNodes != null)
                    htmlWriter.RenderBeginTag("b");
                htmlWriter.WriteEncodedText(node.Title);
                if (childNodes != null)
                    htmlWriter.RenderEndTag();
                htmlWriter.RenderEndTag(); // "a"
            }

            // end the "<div>" of this tree node
            htmlWriter.FormatEndTag("\n"); // "div"

            // write the subtree node, if this node has children
            if (childNodes != null)
            {
                if (!basic)
                {
                    // begin the subtree node
                    if (node.InitiallyExpanded)
                    {
                        htmlWriter.FormatBeginTag("div,class,id,style",
                            "Subtree",
                            String.Format("Subtree{0}", node.TopicId),
                                "display: block");
                    }
                    else
                    {
                        htmlWriter.FormatBeginTag("div,class,id", "Subtree",
                            String.Format("Subtree{0}", node.TopicId));
                    }

                    // recurse
                    topicCount += WriteDocBrowserNodes(childNodes, basic,
                        level + 1, htmlWriter);

                    // end the subtree node
                    htmlWriter.FormatEndTag("\n"); // "div"
                }
                else
                {
                    // recurse
                    topicCount += WriteDocBrowserNodes(childNodes, basic,
                        level + 1, htmlWriter);
                }
            }
        }

        return topicCount;
    }

    
App.WriteDocBrowserIndexHtml Method

Writes the DocBrowser *_Index.htm file.

Parameters

docBrowser

The DocBrowser to generate the Index for.

basic

If true, write HTML for the basic (downlevel) version of the DocBrowser. Otherwise write HTML for the regular (DHTML) version.

topicCount

The approximate number of topics in the table of contents.

htmlWriter

Open onto the DocBrowser *_Index.htm file.

    void WriteDocBrowserIndexHtml(DocBrowser docBrowser, bool basic,
        int topicCount, HtmlTextWriterHelper htmlWriter)
    {
        // begin the HTML document
        htmlWriter.BeginHtmlDocument(HtmlDocType.XhtmlTransitional,
            docBrowser.MarkOfTheWeb);

        // begin the "<head>" block
        htmlWriter.FormatBeginTag("head\n");

        // write the stylesheet "<link>" block
        htmlWriter.FormatTag("link,rel,href\n\n", "stylesheet", "_CodeDoc.css");

        // write the "<script>" block if applicable
        if (!basic)
        {
            // write the "<script>" block that loads _DocBrowser.js
            htmlWriter.FormatBeginTag("script,src,type", "_DocBrowser.js",
                "text/javascript");
            htmlWriter.FormatEndTag("\n");

            // write the "<script>" block that redirects to "_SyncToc.htm" if this page is loaded
            // outside the context of a DocBrowser frameset
            htmlWriter.FormatBeginTag("script,type", "text/javascript");
            htmlWriter.WriteLine("if (parent.DocTopic == undefined)");
            htmlWriter.WriteLine("  window.navigate(\"_SyncToc.htm\");");
            htmlWriter.FormatEndTag("\n\n");
        }

        // end the "<head>" block
        htmlWriter.FormatEndTag("\n\n");

        // begin the "<body>" block
        if (!basic)
        {
            htmlWriter.FormatBeginTag(
                "body,id,onLoad,onSelectStart,onMouseDown,onMouseOver,onKeyDown,onFocus",
                "DocBrowserIndex", "return DBI_OnLoad()", "return false",
                "return DBI_OnMouseDown()", "return DBI_OnMouseOver()",
                "return DBI_OnKeyDown()", "return DBI_OnFocus()");
        }
        else
            htmlWriter.FormatBeginTag("body,id", "BasicDocBrowserIndex");

        // the basic version includes a header
        if (basic)
        {
            htmlWriter.FormatBeginTag("table,class,cellpadding,cellspacing",
                "Header_", "0", "0");
            htmlWriter.RenderBeginTag("tr");
            htmlWriter.FormatBeginTag("td,class", "Left_");
            htmlWriter.WriteEncodedText(
                Resources.BasicDocBrowserIndexHeader);
            htmlWriter.FormatEndTag("\n"); // "td"
            htmlWriter.RenderEndTag(); // "tr"
            htmlWriter.FormatEndTag("\n"); // "table"
        }

        // write the body text
        foreach (IndexEntry indexEntry in docBrowser.IndexEntries)
        {
            string url = indexEntry.DocBrowserNode.Url;
            if (indexEntry.Bookmark != null)
                url += "#" + indexEntry.Bookmark;
            if (!basic)
            {
                htmlWriter.FormatBeginTag("div,id,Topic",
                    String.Format("Topic{0}",
                        indexEntry.DocBrowserNode.TopicId), url);
                htmlWriter.RenderBeginTag("span");
                htmlWriter.WriteEncodedText(indexEntry.Label);
                htmlWriter.RenderEndTag(); // "span"
                htmlWriter.FormatEndTag("\n"); // "div"
            }
            else
            {
                htmlWriter.RenderBeginTag("div");
                htmlWriter.FormatBeginTag("a,target,href", "DocTopic", url);
                htmlWriter.WriteEncodedText(indexEntry.Label);
                htmlWriter.RenderEndTag(); // "a"
                htmlWriter.FormatEndTag("\n"); // "div"
            }
        }

        // end the "<body>" block
        htmlWriter.FormatEndTag("\n\n");

        // end the HTML document
        htmlWriter.EndHtmlDocument();
    }

    
App.WriteDocBrowserIndexXml Method

Writes the DocBrowser *_Index.xml file.

Parameters

docBrowser

The DocBrowser to generate the Index for.

xmlWriter

Open onto the DocBrowser *_Index.xml file.

    void WriteDocBrowserIndexXml(DocBrowser docBrowser, XmlWriter xmlWriter)
    {
        // begin the index XML file
        xmlWriter.WriteStartDocument();
        xmlWriter.WriteStartElement("Index",
            "urn:schemas-dwell-net:codedoc:project-file");
        xmlWriter.WriteString("\n");

        // loop once for index entry
        foreach (IndexEntry indexEntry in docBrowser.IndexEntries)
        {
            xmlWriter.WriteStartElement("IndexEntry");
            xmlWriter.WriteAttributeString("TopicId",
                XmlConvert.ToString(indexEntry.DocBrowserNode.TopicId));
            xmlWriter.WriteAttributeString("Url",
                indexEntry.DocBrowserNode.Url);
            xmlWriter.WriteValue(indexEntry.Label);
            xmlWriter.WriteEndElement();
            xmlWriter.WriteString("\n");
        }

        // end the index XML file
        xmlWriter.WriteEndElement();
    }

    
App.AddTopicsToDocBrowserContents Method

Adds Topic objects from m_docSet to the Contents view of a given DocBrowser. Also updates the Index view.

Parameters

initiallyExpand

Specifies which nodes to initially expand, if any.

titleReplacements

If not null, this is a list of namespace title Regex/replacement-title pairs that will be applied to the title of each namespace node. If null, the namespace topic title is used.

indexLabelSuffix

If not null, this string is appended to the label of each index entry added by this method.

parentDocBrowserNode

The DocBrowserNode that the topics of m_docSet will be added to as child nodes.

codeHtml

If true, links to topics are made to formatted source code pages (i.e. "<WriteFormattedCode>" in the CodeDoc XML project file) rather than to documentation pages (i.e. "<WriteDocumentation>" in the CodeDoc XML project file).

    void AddTopicsToDocBrowserContents(InitiallyExpand initiallyExpand,
        List<KeyValuePair<Regex, string>> titleReplacements,
        string indexLabelSuffix, DocBrowserNode parentDocBrowserNode,
        bool codeHtml)
    {
        // set <docBrowser> to the DocBrowser containing <parentDocBrowserNode>
        DocBrowser docBrowser = parentDocBrowserNode.DocBrowser;

        // loop once for each namespace
        List<Topic> memberTopics = new List<Topic>(100);
        foreach (NamespaceTopic namespaceTopic in
            m_docSet.GetSortedNamespaceTopics())
        {
            // skip this namespace if it's not documented
            if (!namespaceTopic.IsDocumented)
                continue;

            // set <namespaceTitle> to the title of the namespace
            string namespaceTitle = namespaceTopic.LongTitle;
            if (titleReplacements != null)
            {
                foreach (KeyValuePair<Regex, string> replacement in
                    titleReplacements)
                {
                    namespaceTitle = replacement.Key.Replace(namespaceTitle,
                        replacement.Value);
                }
            }

            // set <namespaceNode> to a new DocBrowserNode object
            // correpsonding to <namespaceTopic>
            string url = (codeHtml ? namespaceTopic.CodeHtmlUrl
                : namespaceTopic.DocHtmlFileName);
            //Debug.Assert(url != null);
            DocBrowserNode namespaceNode = new DocBrowserNode(null,
                namespaceTopic.Id, null, url, url, namespaceTitle,
                (initiallyExpand >= InitiallyExpand.Namespaces));

            // add <namespaceNode> as a child of <parentDocBrowserNode>
            parentDocBrowserNode.AddChild(namespaceNode);

            // add index entries for <namespaceNode>
            AddNodeIndexEntries(docBrowser, namespaceNode, namespaceTopic,
                indexLabelSuffix);

            // loop once for each type within the namespace
            foreach (Topic typeTopic in namespaceTopic.TypeTopics)
            {
                // skip this type if it's not documented
                if (!typeTopic.IsDocumented)
                    continue;

                // skip this topic if it an overload list topic
                if (typeTopic is OverloadListTopic)
                    continue;

                // set <typeNode> to a new DocBrowserNode object
                // correpsonding to <typeTopic>
                url = (codeHtml ? typeTopic.CodeHtmlUrl
                    : typeTopic.DocHtmlFileName);
                Debug.Assert(url != null);
                DocBrowserNode typeNode = new DocBrowserNode(null,
                    typeTopic.Id, null, url, url, typeTopic.ShortTitle,
                    (initiallyExpand >= InitiallyExpand.NamespacesAndTypes));

                // add <docBrowserNode> as a child of <namespaceNode>
                namespaceNode.AddChild(typeNode);

                // add index entries for <namespaceNode>
                AddNodeIndexEntries(docBrowser, typeNode, typeTopic,
                    indexLabelSuffix);

                // loop once for each member of this type topic, but skip type
                // topics since we're already enumerating through all type
                // topics
                memberTopics.Clear();
                foreach (Topic memberTopic in
                        Topic.EnumerateDescendentNonTypes(typeTopic))
                    memberTopics.Add(memberTopic);
                memberTopics.Sort();
                foreach (Topic memberTopic in memberTopics)
                {
                    // skip this topic if it's not documented
                    if (!memberTopic.IsDocumented)
                        continue;

                    // skip this topic if it's included in an overload list
                    // topic
                    if (memberTopic.OverloadListTopic != null)
                        continue;

                    // set <memberNode> to a new DocBrowserNode object
                    // correpsonding to <memberTopic>
                    url = (codeHtml ? memberTopic.CodeHtmlUrl
                        : memberTopic.DocHtmlFileName);
                    Debug.Assert(url != null);
                    DocBrowserNode memberNode = new DocBrowserNode(null,
                        memberTopic.Id, null, url, url,
                        memberTopic.VeryShortTitle,
                        (initiallyExpand >= InitiallyExpand.All));

                    // add <docBrowserNode> as a child of <namespaceNode>
                    typeNode.AddChild(memberNode);

                    // add index entries for <namespaceNode>
                    AddNodeIndexEntries(docBrowser, memberNode, memberTopic,
                        indexLabelSuffix);
                }
            }
        }
    }

    
App.AddNodeIndexEntries Method

Adds index entries related to a Topic to a DocBrowser.

Parameters

docBrowser

The DocBrowser to add index entries to.

docBrowserNode

The DocBrowserNode that topic is related to.

topic

The topic to add index entries for.

indexLabelSuffix

If not null, this string is appended to the label of each index entry added by this method.

    void AddNodeIndexEntries(DocBrowser docBrowser,
        DocBrowserNode docBrowserNode, Topic topic, string indexLabelSuffix)
    {
        // add an index topic using Topic.ShortTitle as the label
        string label = topic.ShortTitle;
        if (indexLabelSuffix != null)
            label += indexLabelSuffix;
        docBrowser.AddIndexEntry(label, docBrowserNode, null);

        // add an index topic using Topic.VeryShortTitle as the label
        label = topic.VeryShortTitle;
        if (topic.ParentTopic != null)
        {
            // avoid e.g. "Foo - Foo" (e.g. constructor)
            if (topic.ParentTopic.MemberName != topic.MemberName)
                label += " - " + topic.ParentTopic.MemberName;
        }
            
        if (indexLabelSuffix != null)
            label += indexLabelSuffix;
        docBrowser.AddIndexEntry(label, docBrowserNode, null);
    }

    
App.ToString Method

Returns the result from calling ToString on a value, or String.Empty if that value is null.

Parameters

value

    static string ToString(object value)
    {
        if (value == null)
            return String.Empty;
        else
            return value.ToString();
    }

    
App.m_docSet_AddingSourceFile Method

Called when a source file is about to be added to the documentation set m_docSet.

Parameters

sourceFileAbsolutePath

The value of the SourceFileAbsolutePath property of the new SourceFile object.

sourceFileRelativePath

The value of the SourceFileRelativePath property of the new SourceFile object.

    void m_docSet_AddingSourceFile(string sourceFileAbsolutePath,
        string sourceFileRelativePath)
    {
        // provide user feedback that a source file is being loaded
        if (m_verbose >= 1)
            Console.WriteLine(Resources.Loading, sourceFileRelativePath);
    }

    
App.m_docSet_AddedSourceFile Method

Called when a source file has been successfully added to the documentation set m_docSet.

Parameters

sourceFile

The newly-added SourceFile object.

    void m_docSet_AddedSourceFile(SourceFile sourceFile)
    {
        // display warnings to the user (if any)
        foreach (ParsingException ex in sourceFile.Warnings)
        {
            Console.Error.WriteLine(ex.WarningMessage);
            m_warningCount++;
        }
    }

    
App.m_docSet_ParsingError Method

Called when an error occurs while trying to parse a source file.

Parameters

exception

Information about the error.

    void m_docSet_ParsingError(ParsingException exception)
    {
        Console.Error.WriteLine(exception.WarningMessage);
        m_warningCount++;
    }

    
App.m_docSet_DuplicateTopicTitle Method

Called when two topics are found to have the same Topic.LongTitle. This may indicate that the same source file was added twice to the documentation set.

Parameters

topic1

The first topic.

topic2

The second topic.

    void m_docSet_DuplicateTopicTitle(Topic topic1, Topic topic2)
    {
        Console.Error.WriteLine(Resources.DuplicateTopicTitle,
            topic1.LongTitle, GetTopicSourceFileLabel(topic1),
            GetTopicSourceFileLabel(topic2));
        m_warningCount++;
    }

    
App.m_docSet_DuplicateCanonicalName Method

Called when two topics are found to have the same Topic.CanonicalName. This is purely informational -- it indicates that an overload topic needed to be created since a reference to the canonical name does not resolve to a unique topic.

Parameters

topic1

The first topic.

topic2

The second topic.

    void m_docSet_DuplicateCanonicalName(Topic topic1, Topic topic2)
    {
        string canonicalName = topic1.CanonicalName.ToString();
        int count;
        if (m_overloadLists.TryGetValue(canonicalName, out count))
            m_overloadLists[canonicalName] = count + 1;
        else
        {
            // the first event indicates a duplicate canonical name, so you
            // might think the value below should be 2 instead of 1 -- however,
            // this event is also fired for the overload list topic itself
            m_overloadLists[canonicalName] = 1;
        }
    }

    
App.m_docSet_ReferenceNotFound Method

Called when a topic is found to made a potential reference to another topic, and that reference wasn't found. This is informational, and doesn't necessarily indicate an error.

Parameters

reference

The presumed (but failed) qualified or unqualified member name of the topic being referenced.

contextTopic

The topic containing the reference.

lineNumber

The line number of the reference, within the source file containing contextTopic.

settings

Settings used to resolve the reference.

    void m_docSet_ReferenceNotFound(string reference, Topic contextTopic,
        int lineNumber, FindTopicSettings settings)
    {
        if (!IsExternalReference(reference))
        {
            m_referencesNotFound.Add(new ReferenceNotFound(reference,
                contextTopic, lineNumber, settings));
            m_warningCount++;
        }
    }

    
App.topicRenderer_WarningInSourceFile Method

Called when a TopicRenderer reports a warning associated with a given line number in a source file.

Parameters

sourceFile

The souce file that the warning relates to.

lineNumber

The line number within the source file that the warning relates to.

message

The warning message text.

    void topicRenderer_WarningInSourceFile(SourceFile sourceFile,
        int lineNumber, string message)
    {
        Console.Error.WriteLine(Resources.Warning3,
            sourceFile.SourceFileAbsolutePath, lineNumber, message);
        m_warningCount++;
    }

    
App.GetTopicSourceFileLabel Method

Returns a label referring to the source file of a given topic. Returns a label such as "(none)" if there is no source file for the given topic, or if the given topic is null.

Parameters

topic

    static string GetTopicSourceFileLabel(Topic topic)
    {
        if ((topic != null) && (topic.SourceFile != null) &&
                (topic.SourceFile.SourceFileAbsolutePath != null))
            return topic.SourceFile.SourceFileAbsolutePath;
        else
            return Resources.None;
    }

    
App.MatchRegexList Method

Returns true if a given name is matched by any regular expression in a given list.

Parameters

regexList

The list of regular expressions to search.

name

The name to match.

trueIfNullOrEmpty

If true, then true is returned if regexList is null or empty.

    static bool MatchRegexList(List<Regex> regexList, string name,
        bool trueIfNullOrEmpty)
    {
        // return true if the list is empty, if specified
        if (trueIfNullOrEmpty &&
            ((regexList == null) || (regexList.Count == 0)))
            return true;

        // search the list for a regular expression that <name> matches
        return regexList.Find(delegate(Regex regex)
        {
            return regex.Match(name).Success;
        }) != null;
    }

    
App.GetPath Method (string, string)

If a given path is absolute, it's returned; if the path is relative, it's interpreted as being relative to a given file name, converted to an absolute path, and returned.

Parameters

path

The given absolute or relative path.

relativeTo

The full path of a file that path is relative to (if it's a relative path).

    public static string GetPath(string path, string relativeTo)
    {
        if (Path.IsPathRooted(path))
            return path;
        else
            return Path.GetFullPath(Path.Combine(
                Path.GetDirectoryName(relativeTo), path));
    }

    
App.GetPath Method (XmlReader, string, string)

Reads a specified attribute of the current element of an XmlReader and interprets as a path: if the path is absolute, it's returned; if the path is relative, it's interpreted as being relative to a given file name, converted to an absolute path, and returned.

Parameters

xmlReader

The XmlReader to read from.

attributeName

The name of the XML attribute to read; for example, "Path".

relativeTo

The full path of a file that the attribute value is relative to (if the attribute value is a relative path).

Exceptions
Exception type Condition
ProjectFileException

The specified attribute doesn't exist on the current element.

    public static string GetPath(XmlReader xmlReader, string attributeName,
        string relativeTo)
    {
        return GetPath(GetAttribute(xmlReader, attributeName), relativeTo);
    }

    
App.GetAttribute Method (XmlReader, string)

Reads the string value of an attribute of the current element of an XmlReader that's reading the CodeDoc XML project file. Throws a ProjectFileException if the specified attribute doesn't exist.

Parameters

xmlReader

An XmlReader which, on entry, is positioned on an element within the CodeDoc XML project file. This method does not move the position of this reader.

attributeName

The name of the attribute to get the value of.

Exceptions
Exception type Condition
ProjectFileException

The specified attribute doesn't exist on the current element.

    static string GetAttribute(XmlReader xmlReader, string attributeName)
    {
        string value = xmlReader.GetAttribute(attributeName);
        if (value == null)
        {
            throw new ProjectFileException(Resources.ProjectFileMissingAttr,
                attributeName, xmlReader.Name);
        }
        else
            return value;
    }

    
App.GetAttribute Method (XmlReader, string, string)

Reads the string value of an attribute of the current element of an XmlReader that's reading the CodeDoc XML project file. Returns a specified default value if the specified attribute doesn't exist.

Parameters

xmlReader

An XmlReader which, on entry, is positioned on an element within the CodeDoc XML project file. This method does not move the position of this reader.

attributeName

The name of the attribute to get the value of.

defaultValue

The value to returne if the specified attribute doesn't exist.

    static string GetAttribute(XmlReader xmlReader, string attributeName,
        string defaultValue)
    {
        string value = xmlReader.GetAttribute(attributeName);
        if (value == null)
            return defaultValue;
        else
            return value;
    }

    
App.GetAttribute<T> Method (XmlReader, string)

Reads the value of an attribute of the current element of an XmlReader that's reading the CodeDoc XML project file. The value is converted to a specified type. An exception is thrown if the specified attribute doesn't exist.

Type Parameters

T

The type to convert to.

Parameters

xmlReader

An XmlReader which, on entry, is positioned on an element within the CodeDoc XML project file. This method does not move the position of this reader.

attributeName

The name of the attribute to get the value of.

Exceptions
Exception type Condition
ProjectFileException

The specified attribute doesn't exist on the current element, or the attribut can't be converted to the specified type.

    static T GetAttribute<T>(XmlReader xmlReader, string attributeName)
        where T : struct
    {
        string value = GetAttribute(xmlReader, attributeName);
        try
        {
            return XmlConvertString<T>(value);
        }
        catch (FormatException)
        {
            throw new ProjectFileException(Resources.ProjectFileBadAttrType,
                attributeName, xmlReader.Name, typeof(T).Name);
        }
    }

    
App.GetAttribute<T> Method (XmlReader, string, T)

Reads the value of an attribute of the current element of an XmlReader that's reading the CodeDoc XML project file. The value is converted to a specified type. A default value is used if the specified attribute doesn't exist.

Type Parameters

T

The type to convert to.

Parameters

xmlReader

An XmlReader which, on entry, is positioned on an element within the CodeDoc XML project file. This method does not move the position of this reader.

attributeName

The name of the attribute to get the value of.

defaultValue

The value to returne if the specified attribute doesn't exist.

Exceptions
Exception type Condition
ProjectFileException

The specified attribute doesn't exist on the current element, or the attribut can't be converted to the specified type.

    static T GetAttribute<T>(XmlReader xmlReader, string attributeName,
            T defaultValue)
        where T : struct
    {
        string value = GetAttribute(xmlReader, attributeName, null);
        if (value == null)
            return defaultValue;
        try
        {
            return XmlConvertString<T>(value);
        }
        catch (FormatException)
        {
            throw new ProjectFileException(Resources.ProjectFileBadAttrType,
                attributeName, xmlReader.Name, typeof(T).Name);
        }
    }

    
App.GetInitiallyExpandAttribute Method

Reads the InitiallyExpand-typed value of an attribute of the current element of an XmlReader that's reading the CodeDoc XML project file. Returns InitiallyExpand.None if the specified attribute doesn't exist.

Parameters

xmlReader

An XmlReader which, on entry, is positioned on an element within the CodeDoc XML project file. This method does not move the position of this reader.

attributeName

The name of the attribute to get the value of.

    InitiallyExpand GetInitiallyExpandAttribute(XmlReader xmlReader,
        string attributeName)
    {
        string value = GetAttribute(xmlReader, attributeName, "None");
        switch (value)
        {
        default:
        case "None":
            return InitiallyExpand.None;
        case "Namespaces":
            return InitiallyExpand.Namespaces;
        case "NamespacesAndTypes":
            return InitiallyExpand.NamespacesAndTypes;
        case "All":
            return InitiallyExpand.All;
        }
    }

    
App.XmlConvertString Method

Converts a string from a format that's correct for use within an XML file to a CLR type. Only certain CLR types are supported.

Type Parameters

T

The CLR type to convert to.

Parameters

value

The value to convert.

Exceptions
Exception type Condition
FormatException

The specified value can't be converted to the specified type.

    static T XmlConvertString<T>(string value) where T : struct
    {
        if (typeof(T) == typeof(int))
            return (T) (object) XmlConvert.ToInt32(value);
        else
        if (typeof(T) == typeof(double))
            return (T) (object) XmlConvert.ToDouble(value);
        else
        if (typeof(T) == typeof(bool))
            return (T) (object) XmlConvert.ToBoolean(value);
        else
            throw new FormatException();
    }

    
App.GetRegexAttribute Method

Reads the Regex value of an attribute of the current element of an XmlReader that's reading the CodeDoc XML project file.

Parameters

xmlReader

An XmlReader which, on entry, is positioned on an element within the CodeDoc XML project file. This method does not move the position of this reader.

attributeName

The name of the attribute to get the value of.

Exceptions
Exception type Condition
ProjectFileException

The specified attribute doesn't exist on the current element, or the syntax of the attribute value is incorrect.

    static Regex GetRegexAttribute(XmlReader xmlReader, string attributeName)
    {
        string stringValue = GetAttribute(xmlReader, attributeName);
        try
        {
            return new Regex(stringValue);
        }
        catch (ArgumentException)
        {
            throw new ProjectFileException(Resources.ProjectFileBadRegexAttr,
                attributeName, xmlReader.Name, stringValue);
        }
    }

    
App.IsExternalReference Method

Returns true if a given reference is an external reference, i.e. one listed in the <ExternalReferences> secton of the CodeDoc XML project file. External references are not reported as "references not found".

Parameters

reference

The reference to check.

    bool IsExternalReference(string reference)
    {
        bool unused;
        return m_externalReferences.TryGetValue(reference, out unused);
    }

    
App.UnexpectedText Method

Returns a ProjectFileException indicating that the current node of an XmlReader that's reading the CodeDoc XML project file contains text which is unexpected.

Parameters

xmlReader

An XmlReader positioned on an element within the CodeDoc XML project file.

    ProjectFileException UnexpectedText(XmlReader xmlReader)
    {
        string text = xmlReader.ReadContentAsString();
        const int maxLen = 50;
        if (text.Length > maxLen)
            text = text.Substring(0, maxLen) + "...";
        return new ProjectFileException(Resources.ProjectFileUnexpectedText,
            text);
    }

    
App.NextArg Method

Returns the next command-line argument.

Parameters

argEnum

An enumerator of command-line arguments.

    string NextArg(System.Collections.IEnumerator argEnum)
    {
        if (!argEnum.MoveNext())
            throw new UsageException();
        return (string)argEnum.Current;
    }

    
App.NextArgAsInt Method

Returns the next command-line argument as an int.

Parameters

argEnum

An enumerator of command-line arguments.

    int NextArgAsInt(System.Collections.IEnumerator argEnum)
    {
        try
        {
            return int.Parse(NextArg(argEnum));
        }
        catch (FormatException)
        {
            throw new UsageException();
        }
    }

    
App.MakePath Method

Returns a full path to a file or directory given a full path or a path relative to the directory containing the CodeDoc XML project file or, if m_projectFilePath hasn't been set yet, relative to the current directory.

Parameters

fileOrDirName

The name of the file or directory -- an absolute path or a path relative to the directory containing the CodeDoc XML project file or, if m_projectFilePath hasn't been set yet, relative to the current directory.

flags

Contains FileFlags.File if fileOrDirName is allowed to be a file, FileFlags.Directory if fileOrDirName is allowed to be a directory, FileFlags.Nonexistent if the file or directory is allowed to not exist, or any combination of the three as long as at least one of FileFlags.File or FileFlags.Directory is present.

isDir

On exit, contains true if fileOrDirName refers to an existing directory.

Exceptions
Exception type Condition
ProjectFileException

fileOrDirName doesn't refer to a file or directory as specified by fileFlags.

    string MakePath(string fileOrDirName, FileFlags flags, out bool isDir)
    {
        // set <fullPath> to the absolute path of the file or directory named
        // <fileOrDirName>; throw an exception if the file or directory doesn't
        // obey <flags>; set <isDir> to true if <fullPath> is an existing
        // directory
        isDir = false;
        string fullPath;
        try
        {
            if (m_projectFilePath != null)
                fullPath = GetPath(fileOrDirName, m_projectFilePath);
            else
                fullPath = Path.GetFullPath(fileOrDirName);
            if (File.Exists(fullPath))
            {
                // <fullPath> is an existing file -- check if that's allowed
                if ((flags & FileFlags.File) == 0)
                {
                    // directory expected but file found
                    throw new ProjectFileException(Resources.DirectoryExpected,
                        fullPath);
                }
            }
            else
            if (Directory.Exists(fullPath))
            {
                // <fullPath> is an existing directory -- check if that's
                // allowed
                if ((flags & FileFlags.Directory) == 0)
                {
                    // file expected but directory found
                    throw new ProjectFileException(Resources.FileExpected, fullPath);
                }
                isDir = true;
            }
            else
            {
                // <fullPath> doesn't exist -- check if that's allowed
                if ((flags & FileFlags.Nonexistent) == 0)
                {
                    if ((flags & (FileFlags.File | FileFlags.Directory)) ==
                        FileFlags.File)
                    {
                        // file expected but not found
                        throw new ProjectFileException(Resources.FileNotFound,
                            fullPath);
                    }
                    else
                    if ((flags & (FileFlags.File | FileFlags.Directory)) ==
                        FileFlags.Directory)
                    {
                        // directory expected but not found
                        throw new ProjectFileException(Resources.DirectoryNotFound,
                            fullPath);
                    }
                    else
                    {
                        // either file or directory expected but neither found
                        throw new ProjectFileException(
                            Resources.FileOrDirectoryNotFound, fullPath);
                    }
                }
            }
        }
        catch (ArgumentException)
        {
            fullPath = null;
        }
        catch (NotSupportedException)
        {
            fullPath = null;
        }
        catch (PathTooLongException)
        {
            fullPath = null;
        }
        catch (FileNotFoundException)
        {
            fullPath = null;
        }
        catch (DirectoryNotFoundException)
        {
            fullPath = null;
        }
        if (fullPath == null)
        {
            throw new ProjectFileException(Resources.InvalidPath, fileOrDirName);
        }

        return fullPath;
    }

    
App.CreateDirectoryIfNeeded Method

Creates a directory if it doesn't exist already.

Parameters

dirPath

The full path to the directory to create (if it doesn't exist).

    void CreateDirectoryIfNeeded(string dirPath)
    {
        // create the directory if needed; if an error occurs, set <error> to
        // the exception text
        string error;
        try
        {
            if (!Directory.Exists(dirPath))
                Directory.CreateDirectory(dirPath);
            return;
        }
        catch (IOException ex)
        {
            error = ex.Message;
        }
        catch (UnauthorizedAccessException ex)
        {
            error = ex.Message;
        }
        catch (NotSupportedException ex)
        {
            error = ex.Message;
        }

        // throw a more general exception than those above
        throw new ProjectFileException(Resources.CannotCreateDirectory,
            dirPath);
    }

    
App.CopyFileContents Method

Copies the contents of a file. Unlike File.Copy, this method does not copy file attributes such as read-only.

Parameters

sourceFileName

The name of the file to copy.

destFileName

The name of the destination file.

overwrite

true to allow overwriting of the destination file if it exsts, false if not.

    void CopyFileContents(string sourceFileName, string destFileName,
        bool overwrite)
    {
        // if the destination file already exists and the caller didn't
        // specify allowing overwriting, throw an exception
        if (!overwrite && File.Exists(destFileName))
        {
            throw new Exception(String.Format(Resources.DestinationFileExists,
                destFileName));
        }

        // copy the file
        using (FileStream sourceStream = new FileStream(sourceFileName,
            FileMode.Open, FileAccess.Read))
        {
            using (FileStream destStream = new FileStream(destFileName,
                FileMode.Create, FileAccess.Write))
            {
                byte[] buffer = new byte[
                    Math.Min(0x10000, sourceStream.Length)];
                while (true)
                {
                    int bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
                    if (bytesRead == 0)
                        break;
                    destStream.Write(buffer, 0, bytesRead);
                }
            }
        }
    }

    
App.ConvertTextFileToHtml Method

Converts a text file to HTML.

Parameters

sourceFileName

The name of the text file to convert.

destFileName

The name of the destination HTML file.

overwrite

true to allow overwriting of the destination file if it exsts, false if not.

    void ConvertTextFileToHtml(string sourceFileName, string destFileName,
        bool overwrite)
    {
        // if the destination file already exists and the caller didn't
        // specify allowing overwriting, throw an exception
        if (!overwrite && File.Exists(destFileName))
        {
            throw new Exception(String.Format(Resources.DestinationFileExists,
                destFileName));
        }

        // convert the text file to HTML
        using (TextReader textReader = new StreamReader(sourceFileName))
        {
            using (TextWriter textWriter = new StreamWriter(destFileName))
            {
                using (HtmlTextWriterHelper htmlWriter =
                    new HtmlTextWriterHelper(textWriter, null))
                {
                    ConvertTextStreamToHtml(textReader, htmlWriter);
                }
            }
        }
    }

    
App.ConvertTextStreamToHtml Method

Converts a text stream to HTML.

Parameters

textReader

The stream of text to read.

htmlWriter

Where to write the HTML.

    void ConvertTextStreamToHtml(TextReader textReader,
        HtmlTextWriterHelper htmlWriter)
    {
        // begin the HTML document
        htmlWriter.BeginHtmlDocument(HtmlDocType.XhtmlTransitional, true);

        // begin the "<head>" block
        htmlWriter.FormatBeginTag("head\n");

        // write the "<title>" block
        htmlWriter.RenderBeginTag("title");
        htmlWriter.WriteEncodedText(Resources.DefaultContentsTitle);
        htmlWriter.FormatEndTag("\n\n");

        // write the "<link>" element
        htmlWriter.FormatTag("link,rel,href\n", "stylesheet", "_CodeDoc.css");

        // end the "<head>" block
        htmlWriter.FormatEndTag("\n\n");

        // begin the "<body>" block
        htmlWriter.AddAttribute("id", "TextFile");
        htmlWriter.FormatBeginTag("body\n");

        // begin the "<pre>" block
        htmlWriter.FormatBeginTag("pre");

        // write the body text -- loop once for each line in the input file
        StringBuilder buffer = new StringBuilder(1000);
        while (true)
        {
            // read a line of text
            string line = textReader.ReadLine();
            if (line == null)
                break;

            // convert the line to HTML
            htmlWriter.WriteEncodedText(ExpandTabs(line, 4, buffer));
            htmlWriter.WriteLine();
        }

        // end the "<pre>" block
        htmlWriter.FormatEndTag("\n");

        // write the footer
        htmlWriter.FormatBeginTag("div,class", "ContentsFooter");
        htmlWriter.FormatEndTag("\n\n");

        // end the "<body>" block
        htmlWriter.FormatEndTag("\n");

        // end the HTML document
        htmlWriter.EndHtmlDocument();
    }

    
App.ExpandTabs Method

Expands tab characters to spaces.

Parameters

input

tabWidth

The number of columns (spaces) represented by a tab character.

buffer

A StringBuilder to use as a buffer.

    string ExpandTabs(string input, int tabWidth, StringBuilder buffer)
    {
        buffer.Length = 0;
        for (int index = 0; index < input.Length; index++)
        {
            char ch = input[index];
            if (ch == '\t')
            {
                buffer.Append(' ');
                while ((buffer.Length % tabWidth) != 0)
                    buffer.Append(' ');
            }
            else
                buffer.Append(ch);
        }

        return buffer.ToString();
    }

#if false
    /// <summary>
    /// Returns <n>true</n> if two file paths appear to refer to the same file,
    /// or at least to two files that have the same length and last-modified
    /// time.
    /// </summary>
    ///
    /// <param name="path1">The first path.</param>
    ///
    /// <param name="path2">The second path.</param>
    ///
    bool AreFilesEqual(string path1, string path2)
    {
        FileInfo file1 = new FileInfo(path1);
        FileInfo file2 = new FileInfo(path2);
        return ((file1.Length == file2.Length) &&
                (file1.LastWriteTime == file2.LastWriteTime));
    }
#endif

    
App.RemoveDuplicates Method

Returns an output list containing the same values as a given input list, but with duplicate values removed.

Type Parameters

T

The type of element in the list.

Parameters

input

The sorted input list.

    public static List<T> RemoveDuplicates<T>(List<T> input)
    {
        List<T> output = new List<T>(input.Count);
        T previous = default(T);
        bool first = true;
        foreach (T value in input)
        {
            if (first || !value.Equals(previous))
                output.Add(value);
            previous = value;
            first = false;
        }
        return output;
    }

    //////////////////////////////////////////////////////////////////////////
    // Helper Classes & Enums
    //

    
App.ReferenceNotFound Class

Holds information about an unresolved hyperlink reference from one topic to another. This is informational, and doesn't necessarily indicate an error.

    public class ReferenceNotFound : IComparable<ReferenceNotFound>
    {
        //////////////////////////////////////////////////////////////////////
        // Private Fields
        //

        
App.ReferenceNotFound.m_reference Field

Holds the value of the Reference property.

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        string m_reference;

        
App.ReferenceNotFound.m_contextTopic Field

Holds the value of the ContextTopic property.

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        Topic m_contextTopic;

        
App.ReferenceNotFound.m_lineNumber Field

Holds the value of the LineNumber property.

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        int m_lineNumber;

        
App.ReferenceNotFound.m_settings Field

Holds the value of the Settings property.

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        FindTopicSettings m_settings;

        //////////////////////////////////////////////////////////////////////
        // Public Properties
        //

        
App.ReferenceNotFound.Reference Property

The presumed (but failed) qualified or unqualified member name of the topic being referenced.

        public string Reference
        {
            [DebuggerStepThrough]
            get
            {
                return m_reference;
            }
        }

        
App.ReferenceNotFound.ContextTopic Property

The context containing the failed reference.

        public Topic ContextTopic
        {
            [DebuggerStepThrough]
            get
            {
                return m_contextTopic;
            }
        }

        
App.ReferenceNotFound.LineNumber Property

The line number of the reference, within the source file containing ContextTopic.

        public int LineNumber
        {
            [DebuggerStepThrough]
            get
            {
                return m_lineNumber;
            }
        }

        
App.ReferenceNotFound.Settings Property

Settings used to resolved the failed refernce.

        public FindTopicSettings Settings
        {
            [DebuggerStepThrough]
            get
            {
                return m_settings;
            }
        }

        
App.ReferenceNotFound.IsTypeOnly Property

Return true if this is a reference to a type, false if it may be a reference to anything (type, method, etc.)

        public bool IsTypeOnly
        {
            get
            {
                return (m_settings & FindTopicSettings.TypeOnly) != 0;
            }
        }

        //////////////////////////////////////////////////////////////////////
        // Public Methods Fields
        //

        
ReferenceNotFound Constructor

Initializes an instance of this class.

Parameters

reference

The presumed (but failed) qualified or unqualified member name of the topic being referenced.

contextTopic

The topic containing the reference.

lineNumber

The line number of the reference, within the source file containing contextTopic.

settings

Settings used to resolve the reference.

        public ReferenceNotFound(string reference, Topic contextTopic,
            int lineNumber, FindTopicSettings settings)
        {
            m_reference = reference;
            m_contextTopic = contextTopic;
            m_lineNumber = lineNumber;
            m_settings = settings;
        }

        //////////////////////////////////////////////////////////////////////
        // Overrides
        //

        public override string ToString()
        {
            if (IsTypeOnly)
                return String.Format(Resources.TypeOnly, Reference);
            else
                return Reference;
        }

        //////////////////////////////////////////////////////////////////////
        // IComparable Implementation
        //

        public int CompareTo(ReferenceNotFound other)
        {
            int compare;
            if ((compare = Reference.CompareTo(other.Reference)) != 0)
                return compare;
            if ((compare = ((IsTypeOnly ? 1 : 0) - (other.IsTypeOnly ? 1 : 0)))
                    != 0)
                return compare;
            return LineNumber - other.LineNumber;
        }
    }

    
App.UsageException Class

Indicates a command-line usage error.

    class UsageException : Exception
    {
    }

    
App.ProjectFileException Class

Indicates an error in the CodeDoc XML project file.

Remarks

The application is responsible for adding the file name and current line number of the CodeDoc XML project file to the exception message.

    class ProjectFileException : Exception
    {
        
ProjectFileException Constructor

Initializes an instance of this class.

Parameters

format

A formatting string for an error message to include with the exception.

args

Formatting arguments for the error message.

        public ProjectFileException(string format, params object[] args) :
            base(String.Format(format, args))
        {
        }
    }

    
App.FileFlags Enumeration

Information about a file or directory.

Members
Name Description
Directory

Item is a directory.

File

Item is a file.

Nonexistent

Item doesn't exist.

    [Flags]
    enum FileFlags
    {
        
App.FileFlags.File Enumeration Value

Item is a file.

        File = 0x1,

        
App.FileFlags.Directory Enumeration Value

Item is a directory.

        Directory = 0x2,

        
App.FileFlags.Nonexistent Enumeration Value

Item doesn't exist.

        Nonexistent = 0x4
    }
}

DocBrowser Class

Represents a DocBrowser, i.e. an HTML content browser that includes a "Contents" and "Index" view. A DocBrowser object is also the root of the DocBrowserNode hierarchy that comprises the "Contents" view.

[DebuggerDisplay("DocBrowserNode: {Title}")]
class DocBrowser : DocBrowserNode
{
    //////////////////////////////////////////////////////////////////////////
    // Private Fields
    //

    
DocBrowser.m_rootPath Field

Holds the value of the RootPath property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    string m_rootPath;

    
DocBrowser.m_pagesRelativePath Field

Holds the value of the PagesRelativePath property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    string m_pagesRelativePath;

    
DocBrowser.m_pagesAbsolutePath Field

Holds the value of the PagesAbsolutePath property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    string m_pagesAbsolutePath;

    
DocBrowser.m_framesetFileName Field

Holds the value of the FramesetFileName property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    string m_framesetFileName;

    
DocBrowser.m_basicFramesetFileName Field

Holds the value of the BasicFramesetFileName property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    string m_basicFramesetFileName;

    
DocBrowser.m_baseName Field

Holds the value of the BaseName property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    string m_baseName;

    
DocBrowser.m_leftFrameWidth Field

Holds the value of the LeftFrameWidth property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    int m_leftFrameWidth;

    
DocBrowser.m_markOfTheWeb Field

Holds the value of the MarkOfTheWeb property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    bool m_markOfTheWeb;

    
DocBrowser.m_indexEntries Field

Holds the value of the IndexEntries property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    List<IndexEntry> m_indexEntries = new List<IndexEntry>();

    //////////////////////////////////////////////////////////////////////////
    // Public Properties
    //

    
DocBrowser.RootPath Property

Gets the full path to the DocBrowser root directory, i.e. the directory into which the frameset root file(s) will be written. Derived from the "Path" attribute of the "<CreateDocBrowser>" element of the CodeDoc XML project file.

    public string RootPath
    {
        [DebuggerStepThrough]
        get
        {
            return m_rootPath;
        }
    }

    
DocBrowser.PagesRelativePath Property

Gets the path to the DocBrowser pages directory, i.e. the directory into which generated files are written, relative to RootPath. null if the DocBrowser pages directory is the same as the DocBrowser root directory. From the "PagesRelativePath" attribute of the "<CreateDocBrowser>" element of the CodeDoc XML project file.

    public string PagesRelativePath
    {
        [DebuggerStepThrough]
        get
        {
            return m_pagesRelativePath;
        }
    }

    
DocBrowser.PagesAbsolutePath Property

Gets the full path to the DocBrowser pages directory, i.e. the directory into which generated files are written. Based on PagesRelativePath and RootPath.

    public string PagesAbsolutePath
    {
        [DebuggerStepThrough]
        get
        {
            return m_pagesAbsolutePath;
        }
    }

    
DocBrowser.PathFromPagesToRoot Property

Gets the path to the DocBrowser root directory, i.e. the directory into which the root frameset files are written, relative to the DocBrowser pages directory; for example, "../..". null if the DocBrowser pages directory is the same as the DocBrowser root directory.

    public string PathFromPagesToRoot
    {
        get
        {
            if (m_pagesRelativePath == null)
                return null;
            int slashes = Regex.Matches(m_pagesRelativePath, @"[\/]").Count;
            StringBuilder result = new StringBuilder(50);
            while (slashes-- >= 0)
            {
                if (result.Length > 0)
                    result.Append('/');
                result.Append("..");
            }
            return result.ToString();
        }
    }

    
DocBrowser.FramesetFileName Property

Gets the name of the enhanced frameset HTML file. null if no enhanced frameset HTML file will be generated.

    public string FramesetFileName
    {
        [DebuggerStepThrough]
        get
        {
            return m_framesetFileName;
        }
    }

    
DocBrowser.BasicFramesetFileName Property

Gets the name of the basic (downlevel) frameset HTML file. null if no basic frameset HTML file will be generated.

    public string BasicFramesetFileName
    {
        [DebuggerStepThrough]
        get
        {
            return m_basicFramesetFileName;
        }
    }

    
DocBrowser.BaseName Property

Gets the name of the DocBrowser frameset HTML file base; for example, if "BaseName" is "MyApi", the DocBrowser frameset HTML file is named "MyApi.htm".

    public string BaseName
    {
        [DebuggerStepThrough]
        get
        {
            return m_baseName;
        }
    }

    
DocBrowser.LeftFrameWidth Property

Gets the width of the left browser frame (i.e. the Contents/Index frame) of the DocBrowser, in pixels. From the "LeftFrameWidth" attribute of the "<CreateDocBrowser>" element of the CodeDoc XML project file.

    public int LeftFrameWidth
    {
        [DebuggerStepThrough]
        get
        {
            return m_leftFrameWidth;
        }
    }

    
DocBrowser.MarkOfTheWeb Property

Gets a value that indicates whether to include the Mark of the Web on generated HTML pages. See HtmlTextWriterHelper.BeginHtmlDocument for more information.

    public bool MarkOfTheWeb
    {
        [DebuggerStepThrough]
        get
        {
            return m_markOfTheWeb;
        }
    }

    
DocBrowser.IndexEntries Property

Gets the collection of IndexEntry objects that comprise the index extries associated with this DocBrowser.

    public ICollection<IndexEntry> IndexEntries
    {
        [DebuggerStepThrough]
        get
        {
            return new ReadOnlyCollection<IndexEntry>(m_indexEntries);
        }
    }

    
DocBrowser.TabsHtmlFileName Property

Gets the name of the DocBrowser *_Tabs.htm file, i.e. the frame that implements the tabs.

    public string TabsHtmlFileName
    {
        get
        {
            return String.Format("{0}_Tabs.htm", BaseName);
        }
    }

    
DocBrowser.ContentsHtmlFileName Property

Gets the name of the DocBrowser *_Contents.htm file, i.e. what's in the "Contents" tab frame.

    public string ContentsHtmlFileName
    {
        get
        {
            return String.Format("{0}_Contents.htm", BaseName);
        }
    }

    
DocBrowser.IndexHtmlFileName Property

Gets the name of the DocBrowser *_Index.htm file, i.e. what's in the "Index" tab frame.

    public string IndexHtmlFileName
    {
        get
        {
            return String.Format("{0}_Index.htm", BaseName);
        }
    }

    
DocBrowser.IndexXmlFileName Property

Gets the name of the DocBrowser *_Index.xml file (used by other applications).

    public string IndexXmlFileName
    {
        get
        {
            return String.Format("{0}_Index.xml", BaseName);
        }
    }

    
DocBrowser.AllNodes Property

Returns an enumeration of all DocBrowserNode objects in this DocBrowser, excluding the root node.

    public IEnumerable<DocBrowserNode> AllNodes
    {
        get
        {
            return GetDescendentNodes();
        }
    }

    //////////////////////////////////////////////////////////////////////////
    // Public Methods
    //

    
DocBrowser Constructor

Initializes an instance of this class.

Parameters

nodeId

The value to use to initialize the DocBrowserNode.NodeId property.

rootPath

The value to use to initialize the RootPath property.

pagesRelativePath

The value to use to initialize the PagesRelativePath property.

framesetFileName

The value to use to initialize the FramesetFileName property.

basicFramesetFileName

The value to use to initialize the BasicFramesetFileName property.

title

The value to use to initialize the DocBrowserNode.Title property.

baseName

The value to use to initialize the BaseName property.

leftFrameWidth

The value to use to initialize the LeftFrameWidth property.

markOfTheWeb

The value to use to initialize the MarkOfTheWeb property.

    public DocBrowser(string nodeId, string rootPath, string pagesRelativePath,
        string framesetFileName, string basicFramesetFileName, string title,
        string baseName, int leftFrameWidth, bool markOfTheWeb)
        : base(nodeId, 0, null, null, null, title, true)
    {
        m_rootPath = rootPath;
        m_pagesRelativePath = pagesRelativePath;
        if (pagesRelativePath != null)
            m_pagesAbsolutePath = Path.Combine(rootPath, pagesRelativePath);
        else
            m_pagesAbsolutePath = rootPath;
        m_framesetFileName = framesetFileName;
        m_basicFramesetFileName = basicFramesetFileName;
        m_baseName = baseName;
        m_leftFrameWidth = leftFrameWidth;
        m_markOfTheWeb = markOfTheWeb;
    }

    
DocBrowser.AddIndexEntry Method (IndexEntry)

Adds an index entry to this node given an IndexEntry object.

Parameters

indexEntry

The index entry to add.

    internal void AddIndexEntry(IndexEntry indexEntry)
    {
        m_indexEntries.Add(indexEntry);
    }

    
DocBrowser.AddIndexEntry Method (string, DocBrowserNode, string)

Adds an index entry to this node given a label and a DocBrowserNode.

Parameters

label

The label of the index entry, i.e. what's displayed in the index.

docBrowserNode

The DocBrowserNode associated with this index entry.

bookmark

The bookmark of the index entry, or null if none. The bookmark is the part of a Web page's URL after "#".

    internal void AddIndexEntry(string label, DocBrowserNode docBrowserNode,
        string bookmark)
    {
        m_indexEntries.Add(new IndexEntry(label, docBrowserNode, bookmark));
    }

    
DocBrowser.SortIndexEntries Method

Sorts the index entries.

    internal void SortIndexEntries()
    {
        m_indexEntries.Sort();
    }

    
DocBrowser.RemoveDuplicateIndexEntries Method

Sorts the index entries.

    internal void RemoveDuplicateIndexEntries()
    {
        m_indexEntries = App.RemoveDuplicates(m_indexEntries);
    }
}

DocBrowserNode Class

A node in a DocBrowser tree.

[DebuggerDisplay("DocBrowserNode: {Title}")]
class DocBrowserNode
{
    //////////////////////////////////////////////////////////////////////////
    // Private Fields
    //

    
DocBrowserNode.m_nodeId Field

Holds the value of the NodeId property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    string m_nodeId;

    
DocBrowserNode.m_topicId Field

Holds the value of the TopicId property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    int m_topicId;

    
DocBrowserNode.m_originalFilePath Field

Holds the value of the OriginalFilePath property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    string m_originalFilePath;

    
DocBrowserNode.m_fileName Field

Holds the value of the FileName property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    string m_fileName;

    
DocBrowserNode.m_url Field

Holds the value of the Url property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    string m_url;

    
DocBrowserNode.m_title Field

Holds the value of the Title property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    string m_title;

    
DocBrowserNode.m_parentNode Field

Holds the value of the ParentNode property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    DocBrowserNode m_parentNode;

    
DocBrowserNode.m_initiallyExpanded Field

Holds the value of the InitiallyExpanded property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    bool m_initiallyExpanded;

    
DocBrowserNode.m_childNodes Field

Holds the value of the ChildNodes property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    List<DocBrowserNode> m_childNodes;

    //////////////////////////////////////////////////////////////////////////
    // Public Properties
    //

    
DocBrowserNode.NodeId Property

Gets the optional NodeId of this node, an arbitrary string used to refer to the node from elsewhere in the CodeDoc XML project file. From the "NodeId" attribute of a "<CreateDocBrowser>" or "<DocBrowserNode>" element. null if no ID is specified.

    public string NodeId
    {
        [DebuggerStepThrough]
        get
        {
            return m_nodeId;
        }
    }

    
DocBrowserNode.TopicId Property

Gets the optional TopicId of this node, an integer used to uniquely identify the node within the DocBrowser tree. A TopicId value may be automatically generated, or may be manually specified by the "TopicId" attribute of a "<CreateDocBrowser>" or "<DocBrowserNode>" element. Unlike NodeId, which is used primarily as a way of referring to another node within the CodeDoc XML project file, TopicId is used at run time of the DocBrowser HTML/JScript code.

    public int TopicId
    {
        [DebuggerStepThrough]
        get
        {
            return m_topicId;
        }
    }

    
DocBrowserNode.OriginalFilePath Property

Gets the full path of the original file referred to by this DocBrowser node, i.e. the file that will be copied into the DocBrowser directory. Derived from the "Path" attribute of a "<DocBrowserStaticPages>" or "<DocBrowserNode>" element. null for the root node, i.e. the DocBrowserNode object that's also a DocBrowser object; also null for DocBrowser nodes that don't have static original files (e.g. nodes corresponding to XML documentation).

    public string OriginalFilePath
    {
        [DebuggerStepThrough]
        get
        {
            return m_originalFilePath;
        }
    }

    
DocBrowserNode.FileName Property

Gets the name of the content file containing the page/document referred to by this DocBrowser node. This is the name of the file that's stored in the DocBrowser directory, if applicable. In the case of static (non-generated) files, this is derived from the "NewFileName" attribute of a "<DocBrowserStaticPages>" or "<DocBrowserNode>" element, or the "Path" attribute if "NewFileName" isn't specified. FileName may be automatically generated in the case of DocBrowser nodes that don't have static original files (e.g. nodes corresponding to XML documentation).

    public string FileName
    {
        [DebuggerStepThrough]
        get
        {
            return m_fileName;
        }
    }

    
DocBrowserNode.Url Property

Gets the URL of the page/document that the DocBrowser node refers to. The URL is relative to the DocBrowser directory.

    public string Url
    {
        [DebuggerStepThrough]
        get
        {
            return m_url;
        }
    }

    
DocBrowserNode.Title Property

Gets the title of this DocBrowser node. For the root DocBrowser node (i.e. the DocBrowserNode object that's also a DocBrowser object), Title is the title of the DocBrowser itself, i.e. the title that's displayed in the title bar of the browser.

    public string Title
    {
        [DebuggerStepThrough]
        get
        {
            return m_title;
        }
    }

    
DocBrowserNode.ParentNode Property

Gets the parent node of this DocBrowser node, or null if none.

    public DocBrowserNode ParentNode
    {
        [DebuggerStepThrough]
        get
        {
            return m_parentNode;
        }
    }

    
DocBrowserNode.InitiallyExpanded Property

Sets or gets a value indicating whether this DocBrowser node is initially expanded or collapsed.

    public bool InitiallyExpanded
    {
        [DebuggerStepThrough]
        get
        {
            return m_initiallyExpanded;
        }
        set
        {
            m_initiallyExpanded = value;
        }
    }

    
DocBrowserNode.ChildNodes Property

Gets the collection of DocBrowserNode objects that are children of this node; null if none.

    public ICollection<DocBrowserNode> ChildNodes
    {
        [DebuggerStepThrough]
        get
        {
            if (m_childNodes == null)
                return null;
            else
                return new ReadOnlyCollection<DocBrowserNode>(m_childNodes);
        }
    }

    
DocBrowserNode.DocBrowser Property

Gets the DocBrowser containing this DocBrowserNode.

    public DocBrowser DocBrowser
    {
        get
        {
            DocBrowserNode nodeSearch = this;
            while (!(nodeSearch is DocBrowser))
                nodeSearch = nodeSearch.ParentNode;
            return (DocBrowser) nodeSearch;
        }
    }

    //////////////////////////////////////////////////////////////////////////
    // Public Methods
    //

    
DocBrowserNode Constructor

Initializes an instance of this class.

Parameters

nodeId

The value to use to initialize the NodeId property.

topicId

The value to use to initialize the TopicId property.

originalFilePath

The value to use to initialize the OriginalFilePath property.

fileName

The value to use to initialize the FileName property.

url

The value to use to initialize the Url property.

title

The value to use to initialize the Title property.

initiallyExpanded

    public DocBrowserNode(string nodeId, int topicId, string originalFilePath,
        string fileName, string url, string title, bool initiallyExpanded)
    {
        m_nodeId = nodeId;
        m_topicId = topicId;
        m_originalFilePath = originalFilePath;
        m_fileName = fileName;
        m_url = url;
        m_title = title;
        m_initiallyExpanded = initiallyExpanded;
    }

    
DocBrowserNode.GetDescendentNodes Method

Returns an enumeration of all descendent nodes of this node, i.e. all nodes for which this node is a parent, grandparent, etc.

    public IEnumerable<DocBrowserNode> GetDescendentNodes()
    {
        if (m_childNodes == null)
            yield break;
        foreach (DocBrowserNode node in m_childNodes)
        {
            yield return node;
            foreach (DocBrowserNode descendentNode in node.GetDescendentNodes())
                yield return descendentNode;
        }
    }

    //////////////////////////////////////////////////////////////////////////
    // Internal Methods
    //

    
DocBrowserNode.AddChild Method

Adds a child DocBrowserNode to this node.

Parameters

childNode

The node to add.

    internal void AddChild(DocBrowserNode childNode)
    {
        if (m_childNodes == null)
            m_childNodes = new List<DocBrowserNode>();
        m_childNodes.Add(childNode);
        childNode.m_parentNode = this;
    }
}

IndexEntry Class

An entry in a DocBrowser index. Each DocBrowserNode has zero or more associated index entries.

class IndexEntry : IComparable<IndexEntry>
{
    //////////////////////////////////////////////////////////////////////
    // Private Fields
    //

    
IndexEntry.m_label Field

Holds the value of the Label property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    string m_label;

    
IndexEntry.m_docBrowserNode Field

Holds the value of the DocBrowserNode property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    DocBrowserNode m_docBrowserNode;

    
IndexEntry.m_bookmark Field

Holds the value of the Bookmark property.

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    string m_bookmark;

    
IndexEntry.m_sortKey Field

The string to sort by.

    string m_sortKey;

    //////////////////////////////////////////////////////////////////////
    // Public Properties
    //

    
IndexEntry.Label Property

Gets the label of the index entry, i.e. what's displayed in the index.

    public string Label
    {
        [DebuggerStepThrough]
        get
        {
            return m_label;
        }
    }

    
IndexEntry.DocBrowserNode Property

Gets the DocBrowserNode associated with this index entry. Each DocBrowserNode refers to zero or more index entries.

    public DocBrowserNode DocBrowserNode
    {
        [DebuggerStepThrough]
        get
        {
            return m_docBrowserNode;
        }
    }

    
IndexEntry.Bookmark Property

Gets the bookmark of the index entry, or null if none.

    public string Bookmark
    {
        [DebuggerStepThrough]
        get
        {
            return m_bookmark;
        }
    }

    //////////////////////////////////////////////////////////////////////
    // Override Methods
    //

    public override string ToString()
    {
        return Label;
    }

    public override bool Equals(object obj)
    {
        IndexEntry other = obj as IndexEntry;
        if (obj == null)
            return false;
        return StringComparer.OrdinalIgnoreCase.Equals(Label, other.Label) &&
            (DocBrowserNode.TopicId == other.DocBrowserNode.TopicId) &&
            StringComparer.OrdinalIgnoreCase.Equals(Bookmark, other.Bookmark);
    }

    public override int GetHashCode()
    {
        return StringComparer.OrdinalIgnoreCase.GetHashCode(Label) ^
            DocBrowserNode.GetHashCode() ^
            StringComparer.OrdinalIgnoreCase.GetHashCode(Bookmark);
    }

    //////////////////////////////////////////////////////////////////////
    // IComparable<IndexEntry> Implementation
    //

    int IComparable<IndexEntry>.CompareTo(IndexEntry other)
    {
        return StringComparer.Ordinal.Compare(m_sortKey, other.m_sortKey);
    }

    //////////////////////////////////////////////////////////////////////
    // Internal Methods
    //

    
IndexEntry Constructor

Initializes an instance of this class.

Parameters

label

The value of the Label property.

docBrowserNode

The value of the DocBrowserNode property.

bookmark

The value of the Bookmark property.

    internal IndexEntry(string label, DocBrowserNode docBrowserNode, string bookmark)
    {
        // initialize state
        m_label = label;
        m_docBrowserNode = docBrowserNode;
        m_bookmark = bookmark;

        // create a sort key; note that sorting is done by converting
        // the label to lowercase and then doing an ordinal sort -- other
        // methods (e.g. doing a StringComparer.OrdinalIgnoreCase sort)
        // can cause problems (e.g. the characters "[\]^_`" sorting
        // incorrectly in JScript vs. C#)
        m_sortKey = label.ToLower(CultureInfo.InvariantCulture);
    }
}

InitiallyExpand Enumeration

Specifies which nodes of a DocBrowser subtree generated from XML comment documentation are initially expanded.

Members
Name Description
All

All nodes are initially expanded.

Namespaces

Only namespace nodes are initially expanded.

NamespacesAndTypes

Only namespace and type nodes are initially expanded.

None

No nodes are initially expanded.

public enum InitiallyExpand
{
    
InitiallyExpand.None Enumeration Value

No nodes are initially expanded.

    None,

    
InitiallyExpand.Namespaces Enumeration Value

Only namespace nodes are initially expanded.

    Namespaces,

    
InitiallyExpand.NamespacesAndTypes Enumeration Value

Only namespace and type nodes are initially expanded.

    NamespacesAndTypes,

    
InitiallyExpand.All Enumeration Value

All nodes are initially expanded.

    All,
}

}