Created
May 4, 2017 19:30
-
-
Save ZimM-LostPolygon/3e22d0902cb28e487a1affb0a3a309cc to your computer and use it in GitHub Desktop.
A simple SandCastle BuildComponent for replacing unknown type references with links from XML database
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel.Composition.Hosting; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using System.Reflection; | |
using System.Windows.Forms; | |
using System.Xml; | |
using System.Xml.Linq; | |
using System.Xml.XPath; | |
using Microsoft.Ddue.Tools.Targets; | |
using Sandcastle.Core.BuildAssembler; | |
using Sandcastle.Core.BuildAssembler.BuildComponent; | |
// Search for "TODO" to find changes that you need to make to this build component template. | |
namespace ExternalReferenceLinksProvider { | |
/// <summary> | |
/// TODO: Set your build component's unique ID and description in the export attribute in the factory class | |
/// below. | |
/// </summary> | |
/// <remarks> | |
/// The <c>BuildComponentExportAttribute</c> is used to export your component so that the help | |
/// file builder finds it and can make use of it. The example below shows the basic usage for a common | |
/// build component. Multiple copies of build components can be created depending on their usage. The | |
/// host process will create instances as needed and will dispose of them when it is done with them. | |
/// </remarks> | |
public class ExternalReferenceLinksProviderComponent : BuildComponentCore { | |
#region Constructor | |
/// <summary> | |
/// Constructor | |
/// </summary> | |
/// <param name="buildAssembler">A reference to the build assembler</param> | |
protected ExternalReferenceLinksProviderComponent(BuildAssemblerCore buildAssembler) : base(buildAssembler) { | |
} | |
#endregion | |
#region Build component factory for MEF | |
/// <summary> | |
/// This is used to create a new instance of the build component | |
/// </summary> | |
/// <remarks> | |
/// TODO: If not configurable, remove the <c>IsConfigurable</c> property or set it to false. | |
/// The <c>IsVisible</c> property is typically set to true so that the component can be exposed in | |
/// configuration tools such as the Sandcastle Help File Builder. If set to false, the component will | |
/// be hidden but can be used if referenced in a configuration file or as a dependency. | |
/// </remarks> | |
[BuildComponentExport( | |
"ExternalReferenceLinksProvider", | |
IsVisible = true, | |
IsConfigurable = true, | |
Version = AssemblyInfo.ProductVersion, | |
Copyright = AssemblyInfo.Copyright, | |
Description = "ExternalReferenceLinksProvider build component")] | |
public sealed class Factory : BuildComponentFactory { | |
public override string DefaultConfiguration => | |
@" | |
<include path=""FIXME"" /> | |
"; | |
public Factory() { | |
const string componentName = "Multi-format Output Component"; | |
ReferenceBuildPlacement = new ComponentPlacement(PlacementAction.Before, componentName); | |
ConceptualBuildPlacement = new ComponentPlacement(PlacementAction.Before, componentName); | |
} | |
/// <inheritdoc /> | |
public override BuildComponentCore Create() { | |
return new ExternalReferenceLinksProviderComponent(BuildAssembler); | |
} | |
} | |
#endregion | |
private static readonly XPathExpression kReferenceLinkExpression = XPathExpression.Compile("//referenceLink"); | |
private Dictionary<string, string> _qualifiedNameToUrlMap; | |
#region Abstract method implementations | |
//===================================================================== | |
/// <summary> | |
/// Initialize the build component | |
/// </summary> | |
/// <param name="configuration">The component configuration</param> | |
public override void Initialize(XPathNavigator configuration) { | |
Assembly asm = Assembly.GetExecutingAssembly(); | |
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(asm.Location); | |
WriteMessage(MessageLevel.Info, "[{0}, version {1}]\r\n ExternalReferenceLinksProvider Component. {2}", | |
fvi.ProductName, fvi.ProductVersion, fvi.LegalCopyright); | |
_qualifiedNameToUrlMap = new Dictionary<string, string>(); | |
// Load external links from XML | |
foreach (XPathNavigator includeNode in configuration.Select("//include").ToArray()) { | |
string path = includeNode.GetAttribute("path", ""); | |
if (!File.Exists(path)) { | |
WriteMessage(MessageLevel.Warn, $"External reference links file {path} doesn't exists"); | |
continue; | |
} | |
WriteMessage(MessageLevel.Diagnostic, $"Loading external reference links file {path}"); | |
ParseExternalReferenceLinksFile(path, _qualifiedNameToUrlMap); | |
} | |
WriteMessage(MessageLevel.Diagnostic, $"Loaded total of {_qualifiedNameToUrlMap.Count} external reference links"); | |
} | |
/// <summary> | |
/// Apply this build component's changes to the document | |
/// </summary> | |
/// <param name="document">The document to modify</param> | |
/// <param name="key">The document's key</param> | |
public override void Apply(XmlDocument document, string key) { | |
foreach (XPathNavigator linkNode in document.CreateNavigator().Select(kReferenceLinkExpression).ToArray()) { | |
ReferenceLinkInfo link = new ReferenceLinkInfo(linkNode); | |
// Get URL from type name | |
if (_qualifiedNameToUrlMap.TryGetValue(link.Target, out string externalUrl)) { | |
WriteMessage(MessageLevel.Diagnostic, $"For target {link.Target} found external URL: {externalUrl}"); | |
// Replace with <a> tag | |
XmlWriter writer = linkNode.InsertAfter(); | |
writer.WriteStartElement("a"); | |
writer.WriteAttributeString("href", externalUrl); | |
writer.WriteAttributeString("target", "_blank"); | |
string cutTarget = link.Target; | |
if (cutTarget.Length > 2 && cutTarget[1] == ':') { | |
cutTarget = cutTarget.Substring(2); | |
} | |
if (cutTarget.StartsWith("UnityEngine.", StringComparison.InvariantCulture)) { | |
cutTarget = cutTarget.Substring("UnityEngine.".Length); | |
} | |
writer.WriteString(cutTarget); | |
// Write the closing link tag | |
writer.WriteEndElement(); | |
writer.Close(); | |
linkNode.DeleteSelf(); | |
} else { | |
//linkNode.InsertAfter($" ({link.Target})"); | |
} | |
} | |
} | |
#endregion | |
private void ParseExternalReferenceLinksFile(string filePath, IDictionary<string, string> qualifiedNameToUrlMap) { | |
XDocument document = XDocument.Load(filePath); | |
foreach (XElement item in document.Root.DescendantNodes()) { | |
qualifiedNameToUrlMap[item.Attribute("qualifiedname").Value] = item.Attribute("url").Value; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment