using System; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml.Linq; using System.Reflection; namespace SDKDocGenerator.Writers { public abstract class BaseWriter { protected FrameworkVersion _version; public static readonly List FieldTableColumnHeaders = new List { new TableColumnHeader { CssClass = "iconColumn" }, new TableColumnHeader {Title = "Name", CssClass = "nameColumn"}, new TableColumnHeader {Title = "Type", CssClass = "typeColumn"}, new TableColumnHeader {Title = "Description", CssClass = "descriptionColumn"} }; public static readonly List PropertiesTableColumnHeaders = new List { new TableColumnHeader { CssClass = "iconColumn" }, new TableColumnHeader {Title = "Name", CssClass = "nameColumn"}, new TableColumnHeader {Title = "Type", CssClass = "typeColumn"}, new TableColumnHeader {Title = "Description", CssClass = "descriptionColumn"} }; public static readonly List IconisedNameDescriptionTableColumnHeaders = new List { new TableColumnHeader {CssClass = "iconColumn"}, new TableColumnHeader {Title = "Name", CssClass = "nameColumn"}, new TableColumnHeader {Title = "Description", CssClass = "descriptionColumn"} }; public static readonly List NameDescriptionTableColumnHeaders = new List { new TableColumnHeader {Title = "Name", CssClass = "nameColumn"}, new TableColumnHeader {Title = "Description", CssClass = "descriptionColumn"} }; private const string FeedbackSection = "{0}"; public static string BJSDisclaimerTemplate = "AWS services or capabilities described in AWS Documentation may vary by region/location. " + "Click Getting Started with Amazon AWS to see specific differences applicable to the China (Beijing) Region."; public GenerationManifest Artifacts { get; private set; } public AbstractTypeProvider TypeProvider { get; private set; } protected BaseWriter(GenerationManifest artifacts, AbstractTypeProvider typeProvider, FrameworkVersion version) { Artifacts = artifacts; TypeProvider = typeProvider; _version = version; } protected BaseWriter(GenerationManifest artifacts, FrameworkVersion version) : this(artifacts, artifacts.ManifestAssemblyContext.SdkAssembly, version) { } public string BJSRegionDisclaimer { get { return string.Format(BJSDisclaimerTemplate, Artifacts.Options.BJSDocsDomain); } } protected abstract string GetTitle(); protected abstract string GetMemberName(); protected abstract string GetMemberType(); protected abstract string GenerateFilename(); protected abstract string GenerateFilepath(); protected abstract XElement GetSummaryDocumentation(); protected virtual void AddSummaryNotes(TextWriter writer) { } protected abstract void WriteContent(TextWriter writer); // the computed relative path(s) to the root of the doc set // (ie to the folder containing ./items) protected string RootRelativePath { get; private set; } public void Write() { var filename = Path.Combine(Artifacts.OutputFolder, GenerateFilepath(), GenerateFilename()); try { RootRelativePath = ComputeRelativePathToRoot(filename); } catch (PathTooLongException) { Console.WriteLine("Path is too long for file : {0}", filename); throw; } var directory = new FileInfo(filename).Directory.FullName; if (!Directory.Exists(directory)) { Console.WriteLine("\t\tcreating directory: {0}", directory); Directory.CreateDirectory(directory); } using (var writer = new StringWriter()) { writer.WriteLine(""); writer.WriteLine(""); writer.WriteLine(""); writer.WriteLine(""); writer.WriteLine(""); writer.WriteLine("", RootRelativePath); writer.WriteLine("", RootRelativePath); writer.WriteLine("", RootRelativePath); writer.WriteLine("", RootRelativePath); // every page needs a title, meta description and canonical url to satisfy indexing. The summary/synopsis // text for an element has proven unreliable as a useful source for info the search results so stay with // the page title for now writer.WriteLine("", GetTitle()); writer.WriteLine("{0} | AWS SDK for .NET V3", GetTitle()); writer.WriteLine(""); writer.WriteLine("", FilenameGenerator.Escape(this.GenerateFilename()), FilenameGenerator.Escape(this.GetTOCID())); writer.WriteLine(""); writer.WriteLine(""); // every page needs two hidden divs giving the search indexer the product title and guide name writer.WriteLine("
AWS SDK Version 3 for .NET
"); writer.WriteLine("
API Reference
"); WriteRegionDisclaimer(writer); this.WriteHeader(writer); this.WriteToolbar(writer); writer.WriteLine("
"); this.WriteContent(writer); writer.WriteLine("
"); this.WriteFooter(writer); writer.WriteLine(""); writer.WriteLine(""); // normalize all line endings so any docs committed into Git present a consistent // set of line terminators for core.autocrlf to work with var content = new StringBuilder(writer.ToString()); content.Replace("\r\n", "\n").Replace("\n", "\r\n"); using (var fileWriter = new StreamWriter(filename)) { fileWriter.Write(content); } } } protected virtual void WriteRegionDisclaimer(TextWriter writer) { // comment disclaimer is used by DCA pipeline only at present writer.WriteLine(""); // the BJS disclaimer uses its own div with js/css control of // visibility instead of its own pipeline (currently) and that // div needs to be suppressed from the dca-deployed docs writer.WriteLine(""); writer.WriteLine("
"); writer.WriteLine("

{0}

", BJSRegionDisclaimer); writer.WriteLine("
"); writer.WriteLine(""); } protected virtual void WriteHeader(TextWriter writer) { writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("

{0}

", this.GetMemberName()); if (this.GetMemberType() != null) writer.WriteLine("

{0}

", this.GetMemberType()); writer.WriteLine("
"); writer.WriteLine("
"); } protected virtual void WriteToolbar(TextWriter writer) { writer.WriteLine("
"); writer.WriteLine(""); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine(""); writer.WriteLine(""); writer.WriteLine(""); writer.WriteLine(""); writer.WriteLine("
"); writer.WriteLine(""); writer.WriteLine("", RootRelativePath); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine(""); writer.WriteLine(""); writer.WriteLine(""); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine(""); writer.WriteLine("
"); } protected virtual void WriteFooter(TextWriter writer) { writer.WriteLine("
"); writer.WriteLine("Link to this page", this.GenerateFilepath(), FilenameGenerator.Escape(this.GenerateFilename()), FilenameGenerator.Escape(this.GetTOCID())); writer.WriteLine(" "); writer.WriteLine(FeedbackSection, GenerateFeedbackHTML()); writer.WriteLine(""); writer.WriteLine("
"); WriteScriptFiles(writer); } protected abstract string GetTOCID(); private string ComputeRelativePathToRoot(string filePath) { var docsRootFolder = Path.GetDirectoryName(Artifacts.OutputFolder); // trim ./items var pathFromDocsRoot = Path.GetDirectoryName(filePath).Substring(docsRootFolder.Length + 1); var pathComponents = pathFromDocsRoot.Split('\\'); var rel = new StringBuilder(); for (var i = 0; i < pathComponents.Length; i++) { if (i > 0) rel.Append("/"); rel.Append(".."); } return rel.ToString(); } private string GenerateFeedbackHTML() { var filename = FilenameGenerator.Escape(Path.GetFileNameWithoutExtension(GenerateFilename())); const string baseUrl = "https://docs.aws.amazon.com/forms/aws-doc-feedback"; var queryString = string.Format("?service_name={0}&file_name={1}", "NET-Ref-V3", // service_name filename // guide_name ); var fullUrl = baseUrl + queryString; const string feedbackContentFormat = "" + "" + "Did this page help you?  " + "Yes  " + "No   " + "Tell us about it..." + "" + ""; string feedbackContent = string.Format(feedbackContentFormat, filename, fullUrl); return feedbackContent; } protected virtual void WriteScriptFiles(TextWriter writer) { var isCore = Artifacts.ServiceName.Equals("Core", StringComparison.OrdinalIgnoreCase); writer.WriteLine("", RootRelativePath); writer.WriteLine(""); writer.WriteLine("", RootRelativePath); writer.WriteLine("", RootRelativePath); writer.WriteLine("", RootRelativePath); writer.WriteLine(""); writer.WriteLine(""); writer.WriteLine(""); writer.WriteLine("", RootRelativePath); writer.WriteLine("", RootRelativePath); writer.WriteLine("", RootRelativePath); writer.WriteLine("", RootRelativePath); writer.WriteLine(""); } protected string FormatParameters(IList infos) { var sb = new StringBuilder(); foreach (var info in infos) { if (sb.Length > 0) sb.Append(", "); string parameterTypeName; switch (info.ParameterType.FullName) { case "System.String": parameterTypeName = "string"; break; case "System.Int32": parameterTypeName = "int"; break; case "System.Double": parameterTypeName = "double"; break; case "System.Float": parameterTypeName = "float"; break; case "System.Boolean": parameterTypeName = "bool"; break; case "System.Object": parameterTypeName = "object"; break; default: parameterTypeName = info.ParameterType.GetDisplayName(false); break; } sb.AppendFormat("{0}{1}", info.IsOut ? "out " : "", parameterTypeName); } return sb.ToString(); } protected void AddMemberTableSectionHeader(TextWriter writer, string name, bool showIconColumn = true) { AddMemberTableSectionHeader(writer, name, showIconColumn ? IconisedNameDescriptionTableColumnHeaders : NameDescriptionTableColumnHeaders); } /// /// Adds a standard member-name/description table with optional type column /// /// /// /// protected void AddMemberTableSectionHeader(TextWriter writer, string title, List columnHeaders) { writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("

{0}

", title, title.Replace(" ", "").ToLower()); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine(""); writer.WriteLine(""); writer.WriteLine(""); foreach (var ch in columnHeaders) { writer.Write(""); if (!string.IsNullOrEmpty(ch.Title)) writer.Write(ch.Title); writer.Write(""); } writer.WriteLine(""); } protected void AddMemberTableSectionClosing(TextWriter writer) { writer.WriteLine(""); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("
"); } protected void AddSectionHeader(TextWriter writer, string name) { writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("

{0}

", name, name.Replace(" ", "").ToLower()); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("
"); } protected void AddSectionClosing(TextWriter writer) { writer.WriteLine("
"); writer.WriteLine("
"); } protected void AddSummaryDocumentation(TextWriter writer) { writer.WriteLine("
"); var element = GetSummaryDocumentation(); if (element != null) { var htmlDocs = NDocUtilities.TransformDocumentationToHTML(element, "summary", TypeProvider, this._version); writer.WriteLine(htmlDocs); AddSummaryNotes(writer); } writer.WriteLine("
"); } protected void AddRemarksDocumentation(TextWriter writer) { var element = GetSummaryDocumentation(); if (element != null) { var htmlDocs = NDocUtilities.TransformDocumentationToHTML(element, "remarks", TypeProvider, this._version); if (string.IsNullOrEmpty(htmlDocs)) return; AddSectionHeader(writer, "Remarks"); writer.WriteLine(htmlDocs); AddSectionClosing(writer); } } protected void AddExamples(TextWriter writer) { var element = GetSummaryDocumentation(); if (element != null) { var htmlDocs = NDocUtilities.TransformDocumentationToHTML(element, "example", TypeProvider, this._version); if (string.IsNullOrEmpty(htmlDocs)) return; AddSectionHeader(writer, "Examples"); writer.WriteLine(htmlDocs); AddSectionClosing(writer); } } protected void AddSeeAlso(TextWriter writer) { var element = GetSummaryDocumentation(); if (element != null) { var htmlDocs = NDocUtilities.TransformDocumentationToHTML(element, "seealso", TypeProvider, this._version); if (string.IsNullOrEmpty(htmlDocs)) return; AddSectionHeader(writer, "See Also"); writer.WriteLine(htmlDocs); AddSectionClosing(writer); } } protected void AddNamespace(TextWriter writer, string ns, string moduleName) { writer.WriteLine("
"); writer.Write("

"); writer.Write("Namespace: {0}
", ns); writer.Write("Assembly: {0}", moduleName); writer.Write(""); writer.Write("
Version: 3.x.y.z"); writer.Write("
"); writer.Write("

"); writer.WriteLine("
"); } protected void AddVersionInformation(TextWriter writer, AbstractWrapper wrapper) { AddSectionHeader(writer, "Version Information"); var docs35 = NDocUtilities.FindDocumentation(Artifacts.NDocForPlatform("net35"), wrapper); var docs45 = NDocUtilities.FindDocumentation(Artifacts.NDocForPlatform("net45"), wrapper); var docsCore20 = NDocUtilities.FindDocumentation(Artifacts.NDocForPlatform("netstandard2.0"), wrapper); var docsNetCoreApp31 = NDocUtilities.FindDocumentation(Artifacts.NDocForPlatform("netcoreapp3.1"), wrapper); // If there is no documentation then assume it is available for all platforms. var boolNoDocs = docs35 == null && docs45 == null && docsCore20 == null && docsNetCoreApp31 == null; // .NET Core App var netCoreAppVersions = new List(); if (boolNoDocs || (wrapper != null && docsNetCoreApp31 != null)) netCoreAppVersions.Add("3.1"); if(netCoreAppVersions.Count > 0) { writer.WriteLine("

.NET Core App:
Supported in: {0}
", string.Join(", ", netCoreAppVersions)); } // .NET Standard var netstandardVersions = new List(); if (boolNoDocs || (wrapper != null && docsCore20 != null)) netstandardVersions.Add("2.0"); if(netstandardVersions.Count > 0) { writer.WriteLine("

.NET Standard:
Supported in: {0}
", string.Join(", ", netstandardVersions)); } // .NET Framework var netframeworkVersions = new List(); if (boolNoDocs || (wrapper != null && docs45 != null)) netframeworkVersions.Add("4.5"); if (boolNoDocs || (wrapper != null && docs35 != null)) { netframeworkVersions.Add("4.0"); netframeworkVersions.Add("3.5"); } if (netframeworkVersions.Count > 0) { writer.WriteLine("

.NET Framework:
Supported in: {0}
", string.Join(", ", netframeworkVersions)); } AddSectionClosing(writer); } protected void AddSyntax(TextWriter writer, string csharpSyntax) { if (string.IsNullOrEmpty(csharpSyntax)) return; AddSectionHeader(writer, "Syntax"); writer.WriteLine("

"); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("C#"); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("
{0}
", csharpSyntax); writer.WriteLine("
"); writer.WriteLine("
"); writer.WriteLine("
"); AddSectionClosing(writer); } public static string GetCrossReferenceTypeName(XElement element) { var node = element.Attribute("cref"); if (node == null) return null; var typeName = node.Value; return typeName; } public void WriteCrossReferenceTagReplacement(TextWriter writer, string typeName) { var replacement = CreateCrossReferenceTagReplacement(TypeProvider, typeName, this._version); writer.Write(replacement); } public static string CreateCrossReferenceTagReplacement(AbstractTypeProvider typeProvider, string crefTypeName, FrameworkVersion version) { const string amazonNamespaceRoot = "Amazon."; var target = string.Empty; string url = null; string typeName; if (crefTypeName.Length > 2 && crefTypeName[1] == ':') // cref M:, T:, P:, F: indicators typeName = crefTypeName.Substring(2); else typeName = crefTypeName; var typeWrapper = typeProvider.GetType(typeName); if (typeWrapper != null) url = string.Format("./{0}", FilenameGenerator.GenerateFilename(typeWrapper)); else if (typeName.StartsWith("system.", StringComparison.OrdinalIgnoreCase)) { url = string.Format(NDocUtilities.MSDN_TYPE_URL_PATTERN, typeName.ToLower()); target = "target=_new"; } // If we couldn't generate a url to use with an anchor tag, make the typename italic+bold so // that it at least stands out. if (url == null) return string.Format("{0}", typeName); // If the type is one of ours, strip the namespace from the display text to condense things // a little if (typeName.StartsWith(amazonNamespaceRoot, StringComparison.Ordinal)) { var lastPeriodIndex = typeName.LastIndexOf('.'); typeName = typeName.Substring(lastPeriodIndex + 1); } return string.Format("{1}", url, typeName, target); } } /// /// Used to build ordered collections of table headers used in the various /// sections of a page. Custom css (as id or class) can optionally be applied. /// public class TableColumnHeader { /// /// The colum header displayed to the user. If not specified the column /// will have a blank title. /// public string Title { get; set; } /// /// if set, applied as an 'id' attribute on the resulting td element /// public string Id { get; set; } /// /// If set, applied as a 'class' attribute on the resulting td element /// public string CssClass { get; set; } } }