/// <summary>
        /// This is used to extract table of content information from a file
        /// that will appear in the help file's table of content.
        /// </summary>
        /// <param name="filename">The file from which to extract the
        /// information</param>
        /// <returns>The table of content entry</returns>
        private static TocEntry GetTocInfo(string filename)
        {
            TocEntry tocEntry;
            Encoding enc = Encoding.Default;
            string   content;

            content = BuildProcess.ReadWithEncoding(filename, ref enc);

            tocEntry                = new TocEntry();
            tocEntry.IncludePage    = !reTocExclude.IsMatch(content);
            tocEntry.IsDefaultTopic = reIsDefaultTopic.IsMatch(content);

            Match m = reSortOrder.Match(content);

            if (m.Success)
            {
                tocEntry.SortOrder = Convert.ToInt32(
                    m.Groups["SortOrder"].Value,
                    CultureInfo.InvariantCulture);
            }

            // Get the page title if possible.  If not found, use the filename
            // without the path or extension as the page title.
            m = rePageTitle.Match(content);
            if (!m.Success)
            {
                tocEntry.Title = Path.GetFileNameWithoutExtension(filename);
            }
            else
            {
                tocEntry.Title = HttpUtility.HtmlDecode(
                    m.Groups["Title"].Value).Replace("\r", "").Replace("\n", "");
            }

            // Since we've got the file loaded, see if there are links
            // that need to be resolved when the file is copied, if it
            // contains <pre> blocks that should be colorized, or if it
            // contains tags or shared content items that need replacing.
            tocEntry.HasLinks        = reResolveLinks.IsMatch(content);
            tocEntry.HasCodeBlocks   = reCodeBlock.IsMatch(content);
            tocEntry.NeedsColorizing = reColorizeCheck.IsMatch(content);
            tocEntry.HasProjectTags  = (reProjectTags.IsMatch(content) ||
                                        reSharedContent.IsMatch(content));

            return(tocEntry);
        }
예제 #2
0
        /// <summary>
        /// This is called to fixup the comments for C++ compiler generated
        /// XML comments files.
        /// </summary>
        /// <remarks>The C++ compiler generates method signatures that differ
        /// from the other .NET compilers for methods that take generics as
        /// parameters.  These methods fail to get documented as they do not
        /// match the output of <b>MRefBuilder</b>.  The C# and VB.NET
        /// compilers generate names that do match it and this option is not
        /// needed for comments files generated by them.</remarks>
        public void FixupComments()
        {
            this.Save();
            comments = null;
            members  = null;
            enc      = Encoding.UTF8;

            // Read it with the appropriate encoding
            string content = BuildProcess.ReadWithEncoding(
                sourcePath, ref enc);

            // Strip out "`" followed by digits and "^" in member names
            content = reFixupComments1.Replace(content, "$1");
            content = reFixupComments2.Replace(content, "$1");

            // Write the file back out using its original encoding
            using (StreamWriter sw = new StreamWriter(sourcePath,
                                                      false, enc))
            {
                sw.Write(content);
            }
        }
예제 #3
0
        /// <summary>
        /// This is called to generate the HTML table of content when
        /// creating the website output.
        /// </summary>
        /// <returns>The HTML to insert for the table of content.</returns>
        protected string GenerateHtmlToc()
        {
            XmlDocument   toc;
            Encoding      enc = Encoding.Default;
            StringBuilder sb  = new StringBuilder(2048);

            string content;

            // When reading the file, use the default encoding but detect the
            // encoding if byte order marks are present.
            content = BuildProcess.ReadWithEncoding(workingFolder +
                                                    project.HtmlHelpName + ".hhc", ref enc);

            toc = new XmlDocument();
            toc.LoadXml(content);

            // Get the TOC entries from the first UL tag in the body
            XmlNodeList entries = toc.ChildNodes[0].ChildNodes[0].
                                  ChildNodes[0].ChildNodes;

            this.AppendTocEntry(entries, sb);

            return(sb.ToString());
        }
        /// <summary>
        /// This is called to load an additional content file, resolve links
        /// to namespace content and copy it to the output folder.
        /// </summary>
        /// <param name="sourceFile">The source filename to copy</param>
        /// <param name="destFile">The destination filename</param>
        /// <param name="entry">The entry being resolved.</param>
        private void ResolveLinksAndCopy(string sourceFile, string destFile,
                                         TocEntry entry)
        {
            Encoding enc = Encoding.Default;
            string   content, script, syntaxFile;
            int      pos;

            this.ReportProgress("{0} -> {1}", sourceFile, destFile);

            // When reading the file, use the default encoding but detect the
            // encoding if byte order marks are present.
            content = BuildProcess.ReadWithEncoding(sourceFile, ref enc);

            // Use a regular expression to find and replace all <see>
            // tags with link to the help file content.
            if (entry.HasLinks)
            {
                content = reResolveLinks.Replace(content, linkMatchEval);
            }

            // Expand <code> tags if necessary
            if (entry.HasCodeBlocks)
            {
                content = reCodeBlock.Replace(content, codeBlockMatchEval);
            }

            // Colorize <pre> tags if necessary
            if (entry.NeedsColorizing || entry.HasCodeBlocks)
            {
                // Initialize code colorizer on first use
                if (codeColorizer == null)
                {
                    codeColorizer = new CodeColorizer(
                        shfbFolder + @"Colorizer\highlight.xml",
                        shfbFolder + @"Colorizer\highlight.xsl");
                }

                // Set the path the "Copy" image
                codeColorizer.CopyImageUrl = pathToRoot + "icons/CopyCode.gif";

                // Colorize it and replace the "Copy" literal text with the
                // shared content include item so that it gets localized.
                content = codeColorizer.ProcessAndHighlightText(content);
                content = content.Replace(codeColorizer.CopyText + "</span",
                                          "<include item=\"copyCode\"/></span");
                entry.HasProjectTags = true;

                // Add the links to the colorizer stylesheet and script files
                script = String.Format(CultureInfo.InvariantCulture,
                                       "<link type='text/css' rel='stylesheet' " +
                                       "href='{0}html/highlight.css' />" +
                                       "<script type='text/javascript' " +
                                       "src='{0}html/highlight.js'></script>", pathToRoot);

                pos = content.IndexOf("</head>");

                // Create a <head> section if one doesn't exist
                if (pos == -1)
                {
                    script = "<head>" + script + "</head>";
                    pos    = content.IndexOf("<html>");

                    if (pos != -1)
                    {
                        pos += 6;
                    }
                    else
                    {
                        pos = 0;
                    }
                }

                content = content.Insert(pos, script);

                // Copy the colorizer files if not already there
                if (!File.Exists(workingFolder + @"Output\html\highlight.css"))
                {
                    syntaxFile = shfbFolder + @"Colorizer\highlight.css";
                    File.Copy(syntaxFile, workingFolder +
                              @"Output\html\highlight.css");
                    File.SetAttributes(syntaxFile, FileAttributes.Normal);

                    syntaxFile = shfbFolder + @"Colorizer\highlight.js";
                    File.Copy(syntaxFile, workingFolder +
                              @"Output\html\highlight.js");
                    File.SetAttributes(syntaxFile, FileAttributes.Normal);

                    // This one may exist as the default presentation styles
                    // contain an image by this name.
                    syntaxFile = shfbFolder + @"Colorizer\CopyCode.gif";
                    File.Copy(syntaxFile, workingFolder +
                              @"Output\icons\CopyCode.gif", true);
                    File.SetAttributes(syntaxFile, FileAttributes.Normal);
                }
            }

            // Replace project option tags with project option values
            if (entry.HasProjectTags)
            {
                content = reProjectTags.Replace(content, fieldMatchEval);

                // Shared content items can be nested
                while (reSharedContent.IsMatch(content))
                {
                    content = reSharedContent.Replace(content, contentMatchEval);
                }
            }

            // Write the file back out with the appropriate encoding
            using (StreamWriter sw = new StreamWriter(destFile, false, enc))
            {
                sw.Write(content);
            }
        }
        /// <summary>
        /// This copies additional content files from the specified source
        /// folder to the specified destination folder and builds table of
        /// content entries for them.  If any subfolders are found below the
        /// source folder and the wildcard is "*.*", the subfolders are also
        /// copied recursively.
        /// </summary>
        /// <param name="previewing">Pass true to generate the table of content
        /// collection for previewing without actually copying anything.</param>
        /// <param name="sourcePath">The source path from which to copy.</param>
        /// <param name="destPath">The destination path to which to copy.</param>
        /// <returns>The table of content entry for the folder and all
        /// of its children or null if there is nothing to show in the table
        /// of content for this item.</returns>
        protected TocEntry RecursiveContentCopy(bool previewing,
                                                string sourcePath, string destPath)
        {
            TocEntry tocFolder, tocEntry;

            string[] files;
            string   filename, nameLower, rootPath = workingFolder + @"Output\";
            bool     hasContent = false;

            if (sourcePath == null)
            {
                throw new ArgumentNullException("sourcePath");
            }

            if (destPath == null)
            {
                throw new ArgumentNullException("destPath");
            }

            if (toc == null)
            {
                throw new ArgumentNullException("toc");
            }

            int idx = sourcePath.LastIndexOf('\\');

            string dirName  = sourcePath.Substring(0, idx),
                   fileSpec = sourcePath.Substring(idx + 1);

            tocFolder            = new TocEntry();
            tocFolder.SourceFile = dirName + @"\";
            tocFolder.Title      = dirName.Substring(dirName.LastIndexOf("\\") + 1);

            // Copy all the files in the folder
            files = Directory.GetFiles(dirName, fileSpec);

            foreach (string name in files)
            {
                if (exclusionList.ContainsKey(name))
                {
                    continue;
                }

                filename  = destPath + Path.GetFileName(name);
                nameLower = name.ToLower(CultureInfo.InvariantCulture);

                if (nameLower.EndsWith(".htm") || nameLower.EndsWith(".html"))
                {
                    tocEntry = BuildProcess.GetTocInfo(name);
                }
                else
                {
                    tocEntry = null;
                }

                // If the filename matches the folder name and it's an HTML
                // file, extract the TOC info from it for the root node.
                // If there is no info or its excluded, the folder entry won't
                // have an associated page.  Even if excluded, it will still use
                // the title, default topic, and sort order properties though.
                if (tocEntry != null)
                {
                    if (Path.GetFileNameWithoutExtension(name) == tocFolder.Title)
                    {
                        tocFolder.Title          = tocEntry.Title;
                        tocFolder.IsDefaultTopic = tocEntry.IsDefaultTopic;
                        tocFolder.SortOrder      = tocEntry.SortOrder;
                        tocFolder.SourceFile     = name;
                        hasContent = true;  // In case it's this page only

                        if (tocEntry.IncludePage)
                        {
                            tocFolder.DestinationFile = filename.Remove(0,
                                                                        rootPath.Length);

                            if (tocFolder.IsDefaultTopic)
                            {
                                defaultTopic = tocFolder.DestinationFile;
                            }
                        }
                        else
                        {
                            tocFolder.DestinationFile = null;
                        }
                    }
                    else
                    {
                        if (tocEntry.IncludePage)
                        {
                            tocEntry.SourceFile      = name;
                            tocEntry.DestinationFile = filename.Remove(0,
                                                                       rootPath.Length);
                            tocFolder.Children.Add(tocEntry);
                        }

                        if (tocEntry.IsDefaultTopic)
                        {
                            defaultTopic = tocEntry.DestinationFile;
                        }
                    }
                }

                if (!previewing && !Directory.Exists(destPath))
                {
                    Directory.CreateDirectory(destPath);
                }

                // If the file contains links that need to be resolved,
                // it is handled separately.
                if (!previewing && tocEntry != null &&
                    (tocEntry.HasLinks || tocEntry.HasCodeBlocks ||
                     tocEntry.NeedsColorizing || tocEntry.HasProjectTags))
                {
                    // Figure out the path to the root if needed
                    string[] parts = destPath.Remove(0, rootPath.Length).Split('\\');
                    pathToRoot = String.Empty;

                    for (int part = 0; part < parts.Length - 1; part++)
                    {
                        pathToRoot += "../";
                    }

                    this.ResolveLinksAndCopy(name, filename, tocEntry);
                }
                else
                {
                    this.ReportProgress("{0} -> {1}", name, filename);

                    // All attributes are turned off so that we can delete
                    // it later.
                    if (!previewing)
                    {
                        File.Copy(name, filename, true);
                        File.SetAttributes(filename, FileAttributes.Normal);
                    }
                }
            }

            // For "*.*", copy subfolders too
            if (fileSpec == "*.*")
            {
                string[] subFolders = Directory.GetDirectories(dirName);

                foreach (string folder in subFolders)
                {
                    tocEntry = this.RecursiveContentCopy(previewing,
                                                         folder + @"\*.*", destPath +
                                                         folder.Substring(dirName.Length + 1) + @"\");

                    if (tocEntry != null)
                    {
                        tocFolder.Children.Add(tocEntry);
                    }
                }
            }

            return((hasContent || tocFolder.Children.Count != 0) ?
                   tocFolder : null);
        }
        /// <summary>
        /// This is called to copy the additional content files and build a
        /// list of them for the help file project.
        /// </summary>
        /// <param name="previewing">Pass true to generate the table of content
        /// collection for previewing without actually copying anything.</param>
        /// <remarks>Note that for wilcard content items, the folders are
        /// copied recursively.</remarks>
        protected void CopyAdditionalContent(bool previewing)
        {
            TocEntry tocEntry;
            string   source, filename, dirName, srcLower,
                     rootPath = workingFolder + @"Output\";

            string[] fileList;
            int      idx;

            toc           = new TocEntryCollection();
            exclusionList = new Dictionary <string, string>();

            // Get a list of the exclusions first
            foreach (ContentItem ci in project.AdditionalContent)
            {
                if (!ci.ExcludeItems)
                {
                    continue;
                }

                source = ci.SourcePath;

                if (source.IndexOfAny(new char[] { '*', '?' }) == -1)
                {
                    exclusionList.Add(source, null);
                }
                else
                {
                    idx      = source.LastIndexOf('\\');
                    dirName  = source.Substring(0, idx);
                    filename = source.Substring(idx + 1);

                    fileList = Directory.GetFiles(dirName, filename,
                                                  SearchOption.AllDirectories);

                    foreach (string matchFile in fileList)
                    {
                        exclusionList.Add(matchFile, null);
                    }
                }
            }

            // Now copy the additional content less the excluded files
            foreach (ContentItem ci in project.AdditionalContent)
            {
                if (ci.ExcludeItems)
                {
                    continue;
                }

                source   = ci.SourcePath;
                dirName  = workingFolder + @"Output\" + ci.DestinationPath;
                filename = dirName + Path.GetFileName(source);

                if (source.IndexOfAny(new char[] { '*', '?' }) == -1)
                {
                    if (exclusionList.ContainsKey(source))
                    {
                        continue;
                    }

                    srcLower = source.ToLower(CultureInfo.InvariantCulture);

                    if (srcLower.EndsWith(".htm") || srcLower.EndsWith(".html"))
                    {
                        tocEntry = BuildProcess.GetTocInfo(source);

                        // If not to be included in the TOC, don't add it
                        if (tocEntry.IncludePage)
                        {
                            tocEntry.SourceFile      = source;
                            tocEntry.DestinationFile = filename.Remove(0,
                                                                       rootPath.Length);
                            toc.Add(tocEntry);
                        }

                        if (tocEntry.IsDefaultTopic)
                        {
                            defaultTopic = tocEntry.DestinationFile;
                        }
                    }
                    else
                    {
                        tocEntry = null;
                    }

                    if (!previewing && !Directory.Exists(dirName))
                    {
                        Directory.CreateDirectory(dirName);
                    }

                    // If the file contains links that need to be resolved,
                    // it is handled separately.
                    if (!previewing && tocEntry != null &&
                        (tocEntry.HasLinks || tocEntry.HasCodeBlocks ||
                         tocEntry.NeedsColorizing || tocEntry.HasProjectTags))
                    {
                        // Figure out the path to the root if needed
                        string[] parts = ci.DestinationPath.Split('\\');
                        pathToRoot = String.Empty;

                        for (int part = 0; part < parts.Length - 1; part++)
                        {
                            pathToRoot += "../";
                        }

                        this.ResolveLinksAndCopy(source, filename, tocEntry);
                    }
                    else
                    {
                        this.ReportProgress("{0} -> {1}", source, filename);

                        // All attributes are turned off so that we can delete
                        // it later.
                        if (!previewing)
                        {
                            File.Copy(source, filename, true);
                            File.SetAttributes(filename, FileAttributes.Normal);
                        }
                    }
                }
                else
                {
                    tocEntry = this.RecursiveContentCopy(previewing, source,
                                                         dirName);

                    if (tocEntry != null)
                    {
                        if (ci.DestinationPath.Length != 0)
                        {
                            toc.Add(tocEntry);
                        }
                        else
                        {
                            // If the content is copied to the root, just
                            // add the children to the TOC.
                            foreach (TocEntry te in tocEntry.Children)
                            {
                                toc.Add(te);
                            }
                        }
                    }
                }
            }

            // Sort the additional content entries into the proper order
            toc.Sort();

            codeColorizer = null;
            sharedContent = null;
            exclusionList = null;
        }
예제 #7
0
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="buildProcess">The build process using the topic namer</param>
        public ApiTopicNamer(BuildProcess buildProcess)
        {
            this.buildProcess = buildProcess ?? throw new ArgumentNullException(nameof(buildProcess));
            this.filenames    = new HashSet <string>();
        }
예제 #8
0
        /// <summary>
        /// This is called to determine the default topic for the help file
        /// and insert any additional table of content entries for the
        /// additional content files.
        /// </summary>
        /// <param name="format">The format of the table of content
        /// (HtmlHelp1x, HtmlHelp2x, or Website).</param>
        /// <remarks>In the absence of an additional content item with a
        /// default topic indicator, the default page is determined by
        /// extracting the first entry from the generated table of contents
        /// file.  If an additional content item with a default topic indicator
        /// has been specified, it will be the used instead.  The default
        /// topic is not used by HTML Help 2.x files.</remarks>
        /// <exception cref="ArgumentException">This is thrown if the
        /// format is not <b>HtmlHelp1x</b>, <b>HtmlHelp2x</b>, or
        /// <b>Website</b>.</exception>
        protected void UpdateTableOfContent(HelpFileFormat format)
        {
            Encoding enc = Encoding.Default;

            string tocFile, content;
            bool   tocChanged = false;
            int    endTagPos;

            if (format != HelpFileFormat.HtmlHelp1x &&
                format != HelpFileFormat.HtmlHelp2x &&
                format != HelpFileFormat.Website)
            {
                throw new ArgumentException("The format specified must be " +
                                            "a single format, not a combination", "format");
            }

            // HTML 2.x or HTML 1.x/website
            if (format != HelpFileFormat.HtmlHelp2x)
            {
                tocFile = workingFolder + project.HtmlHelpName + ".hhc";
            }
            else
            {
                tocFile = workingFolder + project.HtmlHelpName + ".hxt";
            }

            // When reading the file, use the default encoding but detect the
            // encoding if byte order marks are present.
            content = BuildProcess.ReadWithEncoding(tocFile, ref enc);

            // We only need the default page for HTML Help 1.x files and
            // websites.
            if (format != HelpFileFormat.HtmlHelp2x)
            {
                // Don't bother if an explicit default has been specified
                if (defaultTopic == null)
                {
                    Match m = reExtractDefTopic.Match(content);

                    if (!m.Success)
                    {
                        throw new BuilderException("Unable to determine " +
                                                   "default page in " + tocFile);
                    }

                    defaultTopic = m.Groups["Filename"].Value;
                }

                // Remove the root namespace container if not wanted and it
                // is there.
                if (!project.RootNamespaceContainer &&
                    re1xRootEntry.IsMatch(content))
                {
                    tocChanged = true;
                    content    = re1xRootEntry.Replace(content, String.Empty, 1);
                    endTagPos  = content.LastIndexOf("</UL>");

                    if (endTagPos != -1)
                    {
                        content = content.Remove(endTagPos, 5);
                    }
                }

                // Reset the encoding as HTML 1.x does not appear to
                // support UTF-8 encoding in the table of content.
                enc        = Encoding.Default;
                tocChanged = true;
            }
            else
            {
                // Remove the root namespace container if not wanted and
                // it is there.
                if (!project.RootNamespaceContainer &&
                    content.LastIndexOf("</HelpTOCNode>") != -1)
                {
                    tocChanged = true;
                    content    = re2xRootEntry.Replace(content, String.Empty, 1);
                    endTagPos  = content.LastIndexOf("</HelpTOCNode>");
                    content    = content.Remove(endTagPos, 14);
                }
            }

            // Update and save table of content with additional items
            if (tocChanged || (toc != null && toc.Count != 0))
            {
                // The additional entries can go in ahead of the namespace
                // documentation entries or after them.
                if (toc != null && toc.Count != 0)
                {
                    if (format != HelpFileFormat.HtmlHelp2x)
                    {
                        if (project.ContentPlacement == ContentPlacement.AboveNamespaces)
                        {
                            content = content.Insert(content.IndexOf(
                                                         "<UL>") + 6, toc.ToString());
                        }
                        else
                        {
                            content = content.Insert(content.LastIndexOf(
                                                         "</UL>"), toc.ToString());
                        }
                    }
                    else
                    {
                        if (project.ContentPlacement == ContentPlacement.AboveNamespaces)
                        {
                            content = content.Insert(content.IndexOf(
                                                         "\"1.0\">") + 8,
                                                     toc.ToString(HelpFileFormat.HtmlHelp2x));
                        }
                        else
                        {
                            content = content.Insert(content.IndexOf(
                                                         "</HelpTOC>"),
                                                     toc.ToString(HelpFileFormat.HtmlHelp2x));
                        }
                    }
                }

                // We'll parse the HTML TOC as an XML file later on to
                // generate the tree view TOC for the website.  This
                // requires some fix-ups to the <param> tags and the
                // removal of the DOCTYPE tag.
                if (format == HelpFileFormat.Website)
                {
                    content = reParamFixUp.Replace(content, "<param$1/>");
                    content = reEncodeLeft.Replace(content, "$1&lt;$3");
                    content = reEncodeRight.Replace(content, "$1&gt;$3");
                    content = content.Remove(0, content.IndexOf("<HTML>"));
                }

                // Write the file back out with the appropriate encoding
                using (StreamWriter sw = new StreamWriter(tocFile, false, enc))
                {
                    sw.Write(content);
                }
            }
        }
예제 #9
0
        /// <summary>
        /// Transform the specified template by inserting the necessary
        /// values into the place holders and saving it to the working folder.
        /// </summary>
        /// <param name="template">The template to transform</param>
        /// <param name="sourceFolder">The folder where the template is
        /// located</param>
        /// <param name="destFolder">The folder in which to save the
        /// transformed file</param>
        /// <returns>The path to the transformed file</returns>
        protected string TransformTemplate(string template,
                                           string sourceFolder, string destFolder)
        {
            Encoding enc = Encoding.Default;
            string   templateText, transformedFile;

            if (template == null)
            {
                throw new ArgumentNullException("template");
            }

            if (sourceFolder == null)
            {
                throw new ArgumentNullException("sourceFolder");
            }

            if (destFolder == null)
            {
                throw new ArgumentNullException("destFolder");
            }

            if (!sourceFolder.EndsWith(@"\"))
            {
                sourceFolder += @"\";
            }

            if (!destFolder.EndsWith(@"\"))
            {
                destFolder += @"\";
            }

            try
            {
                // When reading the file, use the default encoding but
                // detect the encoding if byte order marks are present.
                templateText = BuildProcess.ReadWithEncoding(
                    sourceFolder + template, ref enc);

                // Use a regular expression to find and replace all field
                // tags with a matching value from the project.
                templateText = reField.Replace(templateText, fieldMatchEval);

                transformedFile = destFolder + template;

                // Write the file back out using its original encoding
                using (StreamWriter sw = new StreamWriter(transformedFile,
                                                          false, enc))
                {
                    sw.Write(templateText);
                }
            }
            catch (Exception ex)
            {
                throw new BuilderException(String.Format(
                                               CultureInfo.CurrentCulture,
                                               "Unable to transform template '{0}': {1}", template,
                                               ex.Message), ex);
            }

            return(transformedFile);
        }
예제 #10
0
        /// <summary>
        /// Replace a field tag with a value from the project
        /// </summary>
        /// <param name="match">The match that was found</param>
        /// <returns>The string to use as the replacement</returns>
        private string OnFieldMatch(Match match)
        {
            string replaceWith;

            switch (match.Groups["Field"].Value.ToLower(
                        CultureInfo.InvariantCulture))
            {
            case "shfbfolder":
                replaceWith = shfbFolder;
                break;

            case "projectfolder":
                replaceWith = Path.GetDirectoryName(project.Filename);
                if (replaceWith.Length == 0)
                {
                    replaceWith = Directory.GetCurrentDirectory();
                }

                replaceWith += @"\";
                break;

            case "outputfolder":
                replaceWith = outputFolder;
                break;

            case "workingFolder":
                replaceWith = workingFolder;
                break;

            case "sandcastlepath":
                replaceWith = sandcastleFolder;
                break;

            case "presentationpath":
                replaceWith = presentationFolder;
                break;

            case "presentationstyle":
                replaceWith = project.PresentationStyle;
                break;

            case "hhcpath":
                replaceWith = hhcFolder;
                break;

            case "hxcomppath":
                replaceWith = hxcompFolder;
                break;

            case "dependencies":
                // If there are any, the dependencies have already been
                // copied to the .\DLL sub-folder in the working folder.
                if (project.Dependencies.Count == 0)
                {
                    replaceWith = String.Empty;
                }
                else
                {
                    replaceWith = @"/dep:DLL\*.*";
                }
                break;

            case "docinternals":
                if (project.DocumentInternals || project.DocumentPrivates)
                {
                    replaceWith = "/internal+";
                }
                else
                {
                    replaceWith = String.Empty;
                }
                break;

            case "htmlhelpname":
                replaceWith = project.HtmlHelpName;
                break;

            case "commentfilelist":
                replaceWith = project.Assemblies.CommentFileList(
                    workingFolder);
                break;

            case "helptitle":
                replaceWith = project.HelpTitle;
                break;

            case "htmlenchelptitle":
                replaceWith = HttpUtility.HtmlEncode(project.HelpTitle);
                break;

            case "urlenchelptitle":     // Just replace "&" for now
                replaceWith = project.HelpTitle.Replace("&", "%26");
                break;

            case "rootnamespacetitle":
                replaceWith = project.RootNamespaceTitle;

                if (replaceWith.Length == 0)
                {
                    replaceWith = "<include item=\"rootTopicTitleLocalized\"/>";
                }
                break;

            case "binarytoc":
                replaceWith = project.BinaryTOC ? "Yes" : "No";
                break;

            case "windowoptions":
                // Currently, we use a default set of options and only
                // allow showing or hiding the Favorites tab.
                replaceWith = (project.IncludeFavorites) ? "0x63520" : "0x62520";
                break;

            case "langid":
                replaceWith = language.LCID.ToString(
                    CultureInfo.InvariantCulture);
                break;

            case "language":
                replaceWith = String.Format(CultureInfo.InvariantCulture,
                                            "0x{0:X} {1}", language.LCID, language.NativeName);
                break;

            case "locale":
                replaceWith = language.Name.ToLower(
                    CultureInfo.InvariantCulture);
                break;

            case "copyright":
                // Include copyright info if there is a copyright HREF or
                // copyright text.
                if (project.CopyrightHref.Length != 0 ||
                    project.CopyrightText.Length != 0)
                {
                    replaceWith = "<include item=\"copyright\"/>";
                }
                else
                {
                    replaceWith = String.Empty;
                }
                break;

            case "copyrightinfo":
                if (project.CopyrightHref.Length == 0 &&
                    project.CopyrightText.Length == 0)
                {
                    replaceWith = String.Empty;
                }
                else
                if (project.CopyrightHref.Length == 0)
                {
                    replaceWith = project.DecodedCopyrightText;
                }
                else
                if (project.CopyrightText.Length == 0)
                {
                    replaceWith = project.CopyrightHref;
                }
                else
                {
                    replaceWith = String.Format(
                        CultureInfo.CurrentCulture, "{0} ({1})",
                        project.DecodedCopyrightText,
                        project.CopyrightHref);
                }
                break;

            case "htmlenccopyrightinfo":
                if (project.CopyrightHref.Length == 0 &&
                    project.CopyrightText.Length == 0)
                {
                    replaceWith = String.Empty;
                }
                else
                if (project.CopyrightHref.Length == 0)
                {
                    replaceWith = "<p/>" + HttpUtility.HtmlEncode(
                        project.DecodedCopyrightText);
                }
                else
                if (project.CopyrightText.Length == 0)
                {
                    replaceWith = String.Format(
                        CultureInfo.CurrentCulture,
                        "<p/><a href='{0}'>{0}</a>",
                        HttpUtility.HtmlEncode(
                            project.CopyrightHref));
                }
                else
                {
                    replaceWith = String.Format(
                        CultureInfo.CurrentCulture,
                        "<p/><a href='{0}'>{1}</a>",
                        HttpUtility.HtmlEncode(
                            project.CopyrightHref),
                        HttpUtility.HtmlEncode(
                            project.DecodedCopyrightText));
                }
                break;

            case "copyrighthref":
                replaceWith = project.CopyrightHref;
                break;

            case "htmlenccopyrighthref":
                if (project.CopyrightHref.Length == 0)
                {
                    replaceWith = String.Empty;
                }
                else
                {
                    replaceWith = String.Format(
                        CultureInfo.CurrentCulture,
                        "<a href='{0}'>{0}</a>", HttpUtility.HtmlEncode(
                            project.CopyrightHref));
                }
                break;

            case "copyrighttext":
                if (project.CopyrightText.Length == 0)
                {
                    replaceWith = String.Empty;
                }
                else
                {
                    replaceWith = project.DecodedCopyrightText;
                }
                break;

            case "htmlenccopyrighttext":
                if (project.CopyrightText.Length == 0)
                {
                    replaceWith = String.Empty;
                }
                else
                {
                    replaceWith = HttpUtility.HtmlEncode(
                        project.DecodedCopyrightText);
                }
                break;

            case "comments":
                // Include "send comments" line if feedback e-mail address
                // is specified.
                if (project.FeedbackEMailAddress.Length != 0)
                {
                    replaceWith = "<include item=\"comments\"/>";
                }
                else
                {
                    replaceWith = String.Empty;
                }
                break;

            case "feedbackemailaddress":
                replaceWith = project.FeedbackEMailAddress;
                break;

            case "urlencfeedbackemailaddress":
                if (project.FeedbackEMailAddress.Length == 0)
                {
                    replaceWith = String.Empty;
                }
                else
                {
                    replaceWith = HttpUtility.UrlEncode(
                        project.FeedbackEMailAddress);
                }
                break;

            case "htmlencfeedbackemailaddress":
                if (project.FeedbackEMailAddress.Length == 0)
                {
                    replaceWith = String.Empty;
                }
                else
                {
                    replaceWith = HttpUtility.HtmlEncode(
                        project.FeedbackEMailAddress);
                }
                break;

            case "headertext":
                replaceWith = project.HeaderText;
                break;

            case "footertext":
                replaceWith = project.FooterText;
                break;

            case "preliminary":
                // Include the "preliminary" warning in the header text if wanted
                if (project.Preliminary)
                {
                    replaceWith = "<include item=\"preliminary\"/>";
                }
                else
                {
                    replaceWith = String.Empty;
                }
                break;

            case "defaulttopic":
                replaceWith = defaultTopic;
                break;

            case "webdefaulttopic":
                replaceWith = defaultTopic.Replace(@"\", "/");
                break;

            case "frameworkversion":
                replaceWith = project.FrameworkVersion;

                // For .NET 3.0, Microsoft says to use the .NET 2.0
                // framework files.
                if (replaceWith[0] == '3')
                {
                    replaceWith = FrameworkVersionTypeConverter.LatestMatching("2.0");
                }
                break;

            case "frameworkversionshort":
                replaceWith = project.FrameworkVersion.Substring(0, 3);

                // For .NET 3.0, Microsoft says to use the .NET 2.0
                // framework files.
                if (replaceWith[0] == '3')
                {
                    replaceWith = "2.0";
                }
                break;

            case "help1xprojectfiles":
                replaceWith = BuildProcess.HelpProjectFileList(
                    workingFolder + @"Output\", HelpFileFormat.HtmlHelp1x);
                break;

            case "help2xprojectfiles":
                replaceWith = BuildProcess.HelpProjectFileList(
                    workingFolder + @"Output\", HelpFileFormat.HtmlHelp2x);
                break;

            case "projectlinks":
                replaceWith = project.ProjectLinkType.ToString().ToLower(
                    CultureInfo.InvariantCulture);
                break;

            case "sdklinks":
                replaceWith = project.SdkLinkType.ToString().ToLower(
                    CultureInfo.InvariantCulture);
                break;

            case "htmltoc":
                replaceWith = this.GenerateHtmlToc();
                break;

            case "syntaxfilters":
                replaceWith = String.Empty;

                if ((project.SyntaxFilters & SyntaxFilters.CSharp) != 0)
                {
                    replaceWith += "<generator type=\"Microsoft.Ddue." +
                                   "Tools.CSharpDeclarationSyntaxGenerator\" " +
                                   "assembly=\"" + sandcastleFolder +
                                   "ProductionTools\\SyntaxComponents.dll\" />\r\n";
                }

                if ((project.SyntaxFilters & SyntaxFilters.VisualBasic) != 0)
                {
                    replaceWith += "<generator type=\"Microsoft.Ddue." +
                                   "Tools.VisualBasicDeclarationSyntaxGenerator\" " +
                                   "assembly=\"" + sandcastleFolder +
                                   "ProductionTools\\SyntaxComponents.dll\" />\r\n";
                }

                if ((project.SyntaxFilters & SyntaxFilters.CPlusPlus) != 0)
                {
                    replaceWith += "<generator type=\"Microsoft.Ddue." +
                                   "Tools.CPlusPlusDeclarationSyntaxGenerator\" " +
                                   "assembly=\"" + sandcastleFolder +
                                   "ProductionTools\\SyntaxComponents.dll\" />\r\n";
                }

                if ((project.SyntaxFilters & SyntaxFilters.JSharp) != 0)
                {
                    replaceWith += "<generator type=\"Microsoft.Ddue." +
                                   "Tools.JSharpDeclarationSyntaxGenerator\" " +
                                   "assembly=\"" + sandcastleFolder +
                                   "ProductionTools\\SyntaxComponents.dll\" />\r\n";
                }
                break;

            case "syntaxfiltersdropdown":
                replaceWith = String.Empty;

                // Note that we can't remove the dropdown box if only
                // a single language is selected as script still
                // depends on it.
                if ((project.SyntaxFilters & SyntaxFilters.CSharp) != 0)
                {
                    replaceWith += "<language label=\"CSharp\" " +
                                   "name=\"CSharp\" style=\"cs\" />\r\n";
                }

                if ((project.SyntaxFilters & SyntaxFilters.VisualBasic) != 0)
                {
                    replaceWith += "<language label=\"VisualBasic\" " +
                                   "name=\"VisualBasic\" style=\"vb\" />\r\n";
                }

                if ((project.SyntaxFilters & SyntaxFilters.CPlusPlus) != 0)
                {
                    replaceWith += "<language label=\"ManagedCPlusPlus\" " +
                                   "name=\"ManagedCPlusPlus\" style=\"cs\" />\r\n";
                }

                if ((project.SyntaxFilters & SyntaxFilters.JSharp) != 0)
                {
                    replaceWith += "<language label=\"JSharp\" " +
                                   "name=\"JSharp\" style=\"cs\" />\r\n";
                }
                break;

            case "autodocumentconstructors":
                replaceWith = project.AutoDocumentConstructors.ToString().
                              ToLower(CultureInfo.InvariantCulture);
                break;

            case "showmissingparams":
                replaceWith = project.ShowMissingParams.ToString().
                              ToLower(CultureInfo.InvariantCulture);
                break;

            case "showmissingremarks":
                replaceWith = project.ShowMissingRemarks.ToString().
                              ToLower(CultureInfo.InvariantCulture);
                break;

            case "showmissingreturns":
                replaceWith = project.ShowMissingReturns.ToString().
                              ToLower(CultureInfo.InvariantCulture);
                break;

            case "showmissingsummaries":
                replaceWith = project.ShowMissingSummaries.ToString().
                              ToLower(CultureInfo.InvariantCulture);
                break;

            case "showmissingvalues":
                replaceWith = project.ShowMissingValues.ToString().
                              ToLower(CultureInfo.InvariantCulture);
                break;

            default:
                throw new BuilderException(String.Format(
                                               CultureInfo.CurrentCulture,
                                               "Unknown field tag: '{0}'",
                                               match.Groups["Field"].Value));
            }

            return(replaceWith);
        }