summaryrefslogtreecommitdiffstats
path: root/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveReferenceLinksComponent2.cs
diff options
context:
space:
mode:
Diffstat (limited to 'tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveReferenceLinksComponent2.cs')
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveReferenceLinksComponent2.cs473
1 files changed, 473 insertions, 0 deletions
diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveReferenceLinksComponent2.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveReferenceLinksComponent2.cs
new file mode 100644
index 0000000..31e4349
--- /dev/null
+++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveReferenceLinksComponent2.cs
@@ -0,0 +1,473 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Xml;
+using System.Xml.Schema;
+using System.Xml.XPath;
+
+
+namespace Microsoft.Ddue.Tools {
+
+ // replace the old component with the new one
+ public class ResolveReferenceLinksComponent : ResolveReferenceLinksComponent2 {
+
+ public ResolveReferenceLinksComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { }
+
+ }
+
+ public class ResolveReferenceLinksComponent2 : BuildComponent {
+
+ // instantiation logic
+
+ public ResolveReferenceLinksComponent2 (BuildAssembler assembler, XPathNavigator configuration)
+ : base(assembler, configuration) {
+
+ // base-url is an xpath expression applied against the current document to pick up the save location of the
+ // document. If specified, local links will be made relative to the base-url.
+ string baseUrlValue = configuration.GetAttribute("base-url", String.Empty);
+ if (!String.IsNullOrEmpty(baseUrlValue))
+ baseUrl = XPathExpression.Compile(baseUrlValue);
+
+ // url-format is a string format that is used to format the value of local href attributes. The default is
+ // "{0}.htm" for backwards compatibility.
+ string hrefFormatValue = configuration.GetAttribute("href-format", String.Empty);
+ if (!String.IsNullOrEmpty(hrefFormatValue))
+ hrefFormat = hrefFormatValue;
+
+ // the container XPath can be replaced; this is useful
+ string containerValue = configuration.GetAttribute("container", String.Empty);
+ if (!String.IsNullOrEmpty(containerValue)) XmlTargetCollectionUtilities.ContainerExpression = containerValue;
+
+ targets = new TargetCollection();
+ resolver = new LinkTextResolver(targets);
+
+ XPathNodeIterator targets_nodes = configuration.Select("targets");
+ foreach (XPathNavigator targets_node in targets_nodes) {
+
+ // get target type
+ string typeValue = targets_node.GetAttribute("type", String.Empty);
+ if (String.IsNullOrEmpty(typeValue)) WriteMessage(MessageLevel.Error, "Each targets element must have a type attribute that specifies which type of links to create.");
+
+ LinkType2 type = LinkType2.None;
+ try {
+ type = (LinkType2)Enum.Parse(typeof(LinkType2), typeValue, true);
+ if ((type == LinkType2.Msdn) && (msdn == null)) {
+ WriteMessage(MessageLevel.Info, "Creating MSDN URL resolver.");
+ msdn = new MsdnResolver();
+ }
+ } catch (ArgumentException) {
+ WriteMessage(MessageLevel.Error, String.Format("'{0}' is not a supported reference link type.", typeValue));
+ }
+
+ // get base directory
+ string baseValue = targets_node.GetAttribute("base", String.Empty);
+
+ // get file pattern
+ string filesValue = targets_node.GetAttribute("files", String.Empty);
+ if (String.IsNullOrEmpty(filesValue)) WriteMessage(MessageLevel.Error, "Each targets element must have a files attribute specifying which target files to load.");
+
+ // determine whether to search recursively
+ bool recurse = false;
+ string recurseValue = targets_node.GetAttribute("recurse", String.Empty);
+ if (!String.IsNullOrEmpty(recurseValue)) {
+ if (String.Compare(recurseValue, Boolean.TrueString, true) == 0) {
+ recurse = true;
+ } else if (String.Compare(recurseValue, Boolean.FalseString, true) == 0) {
+ recurse = false;
+ } else {
+ WriteMessage(MessageLevel.Error, String.Format("On the targets element, recurse='{0}' is not an allowed value.", recurseValue));
+ }
+ }
+
+ // turn baseValue and filesValue into directoryPath and filePattern
+ string fullPath;
+ if (String.IsNullOrEmpty(baseValue)) {
+ fullPath = filesValue;
+ } else {
+ fullPath = Path.Combine(baseValue, filesValue);
+ }
+ fullPath = Environment.ExpandEnvironmentVariables(fullPath);
+ string directoryPath = Path.GetDirectoryName(fullPath);
+ if (String.IsNullOrEmpty(directoryPath)) directoryPath = Environment.CurrentDirectory;
+ string filePattern = Path.GetFileName(fullPath);
+
+ // verify that directory exists
+ if (!Directory.Exists(directoryPath)) WriteMessage(MessageLevel.Error, String.Format("The targets directory '{0}' does not exist.", directoryPath));
+
+ // add the specified targets from the directory
+ WriteMessage(MessageLevel.Info, String.Format("Searching directory '{0}' for targets files of the form '{1}'.", directoryPath, filePattern));
+ AddTargets(directoryPath, filePattern, recurse, type);
+
+ }
+
+ WriteMessage(MessageLevel.Info, String.Format("Loaded {0} reference targets.", targets.Count));
+
+ string locale_value = configuration.GetAttribute("locale", String.Empty);
+ if (!String.IsNullOrEmpty(locale_value) && msdn != null) msdn.Locale = locale_value;
+
+ string target_value = configuration.GetAttribute("linkTarget", String.Empty);
+ if (!String.IsNullOrEmpty(target_value)) linkTarget = target_value;
+ }
+
+ private void AddTargets (string directory, string filePattern, bool recurse, LinkType2 type) {
+ string[] files = Directory.GetFiles(directory, filePattern);
+ foreach (string file in files) {
+ AddTargets(file, type);
+ }
+ if (recurse) {
+ string[] subdirectories = Directory.GetDirectories(directory);
+ foreach (string subdirectory in subdirectories) {
+ AddTargets(subdirectory, filePattern, recurse, type);
+ }
+ }
+ }
+
+ private void AddTargets (string file, LinkType2 type) {
+ try {
+ XPathDocument document = new XPathDocument(file);
+ XmlTargetCollectionUtilities.AddTargets(targets, document.CreateNavigator(), type);
+ } catch (XmlSchemaException e) {
+ WriteMessage(MessageLevel.Error, String.Format("The reference targets file '{0}' is not valid. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e)));
+ } catch (XmlException e) {
+ WriteMessage(MessageLevel.Error, String.Format("The reference targets file '{0}' is not well-formed XML. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e)));
+ } catch (IOException e) {
+ WriteMessage(MessageLevel.Error, String.Format("An access error occured while opening the reference targets file '{0}'. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e)));
+ }
+ }
+
+ private string linkTarget = "_blank";
+
+ // target information storage
+
+ private TargetCollection targets;
+
+ private LinkTextResolver resolver;
+
+ private static XPathExpression referenceLinkExpression = XPathExpression.Compile("//referenceLink");
+
+ // WebDocs target url formatting
+
+ private XPathExpression baseUrl;
+
+ private string hrefFormat = "{0}.htm";
+
+ // component logic
+
+ public override void Apply (XmlDocument document, string key) {
+
+ // XmlNodeList link_nodes = document.SelectNodes("//referenceLink");
+ XPathNodeIterator linkIterator = document.CreateNavigator().Select(referenceLinkExpression);
+
+ XPathNavigator[] linkNodes = BuildComponentUtilities.ConvertNodeIteratorToArray(linkIterator);
+
+ foreach (XPathNavigator linkNode in linkNodes) {
+
+ // extract link information
+ ReferenceLinkInfo2 link = ReferenceLinkInfo2.Create(linkNode);
+
+ if (link == null) {
+ WriteMessage(MessageLevel.Warn, "Invalid referenceLink element.");
+ } else {
+
+ // determine target, link type, and display options
+ string targetId = link.Target;
+ DisplayOptions options = link.DisplayOptions;
+ LinkType2 type = LinkType2.None;
+
+ Target target = targets[targetId];
+ if (target == null) {
+ // no such target known; set link type to none and warn
+ type = LinkType2.None;
+ WriteMessage(MessageLevel.Warn, String.Format("Unknown reference link target '{0}'.", targetId));
+ } else {
+ // if overload is prefered and found, change targetId and make link options hide parameters
+ if (link.PreferOverload) {
+ MemberTarget member = target as MemberTarget;
+ if ((member != null) && (!String.IsNullOrEmpty(member.OverloadId))) {
+ Target overloadTarget = targets[member.OverloadId];
+ if (overloadTarget != null) {
+ target = overloadTarget;
+ targetId = overloadTarget.Id;
+ }
+ }
+ options = options & ~DisplayOptions.ShowParameters;
+ }
+
+ // get stored link type
+ type = target.DefaultLinkType;
+
+ // if link type is local or index, determine which
+ if (type == LinkType2.LocalOrIndex) {
+ if ((key != null) && targets.Contains(key) && (target.Container == targets[key].Container)) {
+ type = LinkType2.Local;
+ } else {
+ type = LinkType2.Index;
+ }
+ }
+ }
+
+ // links to this page are not live
+ if (targetId == key) {
+ type = LinkType2.Self;
+ } else if ((target != null) && (key != null) && targets.Contains(key) && (target.File == targets[key].File)) {
+ type = LinkType2.Self;
+ }
+
+ // get msdn endpoint, if needed
+ string msdnUrl = null;
+ if (type == LinkType2.Msdn) {
+ if ((msdn == null) || (msdn.IsDisabled)) {
+ // no msdn resolver
+ } else {
+ msdnUrl = msdn.GetMsdnUrl(targetId);
+ if (String.IsNullOrEmpty(msdnUrl)) {
+ WriteMessage(MessageLevel.Warn, String.Format("MSDN URL not found for target '{0}'.", targetId));
+ }
+ }
+ if (String.IsNullOrEmpty(msdnUrl)) type = LinkType2.None;
+ }
+
+ // write opening link tag and target info
+ XmlWriter writer = linkNode.InsertAfter();
+ switch (type) {
+ case LinkType2.None:
+ writer.WriteStartElement("span");
+ writer.WriteAttributeString("class", "nolink");
+ break;
+ case LinkType2.Self:
+ writer.WriteStartElement("span");
+ writer.WriteAttributeString("class", "selflink");
+ break;
+ case LinkType2.Local:
+ // format link with prefix and/or postfix
+ string href = String.Format(hrefFormat, target.File);
+
+ // make link relative, if we have a baseUrl
+ if (baseUrl != null)
+ href = BuildComponentUtilities.GetRelativePath(href, BuildComponentUtilities.EvalXPathExpr(document, baseUrl, "key", key));
+
+ writer.WriteStartElement("a");
+ writer.WriteAttributeString("href", href);
+ break;
+ case LinkType2.Index:
+ writer.WriteStartElement("mshelp", "link", "http://msdn.microsoft.com/mshelp");
+ writer.WriteAttributeString("keywords", targetId);
+ writer.WriteAttributeString("tabindex", "0");
+ break;
+ case LinkType2.Msdn:
+ writer.WriteStartElement("a");
+ writer.WriteAttributeString("href", msdnUrl);
+ writer.WriteAttributeString("target", linkTarget);
+ break;
+ }
+
+ // write the link text
+ if (String.IsNullOrEmpty(link.DisplayTarget)) {
+ if (link.Contents == null) {
+ if (target != null) {
+ resolver.WriteTarget(target, options, writer);
+ } else {
+ //Console.WriteLine("Attemting to create reference");
+ Reference reference = TextReferenceUtilities.CreateReference(targetId);
+ //Console.WriteLine("Returned");
+ if (reference is InvalidReference) WriteMessage(MessageLevel.Warn, String.Format("Invalid reference link target '{0}'.", targetId));
+ resolver.WriteReference(reference, options, writer);
+ }
+ } else {
+ // write contents to writer
+ link.Contents.WriteSubtree(writer);
+ }
+ } else {
+ //Console.WriteLine("Display target = {0}", link.DisplayTarget);
+ if ((String.Compare(link.DisplayTarget, "content", true) == 0) && (link.Contents != null)) {
+ // Use the contents as an XML representation of the display target
+
+ //Console.WriteLine(link.Contents.NodeType);
+ Reference reference = XmlTargetCollectionUtilities.CreateReference(link.Contents);
+ //Console.WriteLine(reference.GetType().FullName);
+ resolver.WriteReference(reference, options, writer);
+ } if ((String.Compare(link.DisplayTarget, "format", true) == 0) && (link.Contents != null)) {
+ // Use the contents as a format string for the display target
+
+ string format = link.Contents.OuterXml;
+ //Console.WriteLine("format = {0}", format);
+
+ string input = null;
+ StringWriter textStore = new StringWriter();
+ try {
+ XmlWriterSettings settings = new XmlWriterSettings();
+ settings.ConformanceLevel = ConformanceLevel.Fragment;
+
+ XmlWriter xmlStore = XmlWriter.Create(textStore, settings);
+ try {
+ if (target != null) {
+ resolver.WriteTarget(target, options, xmlStore);
+ } else {
+ Reference reference = TextReferenceUtilities.CreateReference(targetId);
+ resolver.WriteReference(reference, options, xmlStore);
+ }
+ } finally {
+ xmlStore.Close();
+ }
+ input = textStore.ToString();
+ } finally {
+ textStore.Close();
+ }
+ //Console.WriteLine("input = {0}", input);
+
+ string output = String.Format(format, input);
+ //Console.WriteLine("output = {0}", output);
+
+ XmlDocumentFragment fragment = document.CreateDocumentFragment();
+ fragment.InnerXml = output;
+ fragment.WriteTo(writer);
+
+ //writer.WriteRaw(output);
+ } else {
+ // Use the display target value as a CER for the display target
+
+ TextReferenceUtilities.SetGenericContext(key);
+ Reference reference = TextReferenceUtilities.CreateReference(link.DisplayTarget);
+ //Console.WriteLine("Reference is {0}", reference.GetType().FullName);
+ resolver.WriteReference(reference, options, writer);
+ }
+ }
+
+ // write the closing link tag
+ writer.WriteEndElement();
+ writer.Close();
+ }
+
+ // delete the original tag
+ linkNode.DeleteSelf();
+
+ }
+
+ }
+
+ // msdn resolver
+
+ private MsdnResolver msdn = null;
+
+
+ }
+
+
+ internal class ReferenceLinkInfo2 {
+
+ // stored data
+
+ private string target;
+
+ private string displayTarget;
+
+ private DisplayOptions options = DisplayOptions.Default;
+
+ private bool preferOverload = false;
+
+ private XPathNavigator contents;
+
+ // data accessors
+
+ public string Target {
+ get {
+ return(target);
+ }
+ }
+
+ public string DisplayTarget {
+ get {
+ return(displayTarget);
+ }
+ }
+
+ public DisplayOptions DisplayOptions {
+ get {
+ return(options);
+ }
+ }
+
+ public bool PreferOverload {
+ get {
+ return(preferOverload);
+ }
+ }
+
+ public XPathNavigator Contents {
+ get {
+ return(contents);
+ }
+ }
+
+ // creation logic
+
+ private ReferenceLinkInfo2 () {}
+
+ public static ReferenceLinkInfo2 Create (XPathNavigator element) {
+ if (element == null) throw new ArgumentNullException("element");
+
+ ReferenceLinkInfo2 info = new ReferenceLinkInfo2();
+
+ info.target = element.GetAttribute("target", String.Empty);
+ if (String.IsNullOrEmpty(info.target)) return(null);
+
+ info.displayTarget = element.GetAttribute("display-target", String.Empty);
+
+ string showContainer = element.GetAttribute("show-container", String.Empty);
+ if (String.IsNullOrEmpty(showContainer)) showContainer = element.GetAttribute("qualified", String.Empty);
+ if (!String.IsNullOrEmpty(showContainer)) {
+ if (String.Compare(showContainer, Boolean.TrueString, true) == 0) {
+ info.options = info.options | DisplayOptions.ShowContainer;
+ } else if (String.Compare(showContainer, Boolean.FalseString, true) == 0) {
+ info.options = info.options & ~DisplayOptions.ShowContainer;
+ } else {
+ return(null);
+ }
+ }
+
+ string showTemplates = element.GetAttribute("show-templates", String.Empty);
+ if (!String.IsNullOrEmpty(showTemplates)) {
+ if (String.Compare(showTemplates, Boolean.TrueString, true) == 0) {
+ info.options = info.options | DisplayOptions.ShowTemplates;
+ } else if (String.Compare(showTemplates, Boolean.FalseString, true) == 0) {
+ info.options = info.options & ~DisplayOptions.ShowTemplates;
+ } else {
+ return(null);
+ }
+ }
+
+ string showParameters = element.GetAttribute("show-parameters", String.Empty);
+ if (!String.IsNullOrEmpty(showParameters)) {
+ if (String.Compare(showParameters, Boolean.TrueString, true) == 0) {
+ info.options = info.options | DisplayOptions.ShowParameters;
+ } else if (String.Compare(showParameters, Boolean.FalseString, true) == 0) {
+ info.options = info.options & ~DisplayOptions.ShowParameters;
+ } else {
+ return(null);
+ }
+ }
+
+
+ string preferOverload = element.GetAttribute("prefer-overload", String.Empty);
+ if (String.IsNullOrEmpty(preferOverload)) preferOverload = element.GetAttribute("auto-upgrade", String.Empty);
+ if (!String.IsNullOrEmpty(preferOverload)) {
+ if (String.Compare(preferOverload, Boolean.TrueString, true) == 0) {
+ info.preferOverload = true;
+ } else if (String.Compare(preferOverload, Boolean.FalseString, true) == 0) {
+ info.preferOverload = false;
+ } else {
+ return(null);
+ }
+ }
+
+ info.contents = element.Clone();
+ if (!info.contents.MoveToFirstChild()) info.contents = null;
+
+ return(info);
+ }
+
+ }
+}