/// <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); }
/// <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); } }
/// <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; }
//===================================================================== /// <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>(); }
/// <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<$3"); content = reEncodeRight.Replace(content, "$1>$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); } } }
/// <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); }
/// <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); }