ResolveConceptualLinksComponent.cs :  » Development » Sandcastle » Microsoft » Ddue » Tools » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Development » Sandcastle 
Sandcastle » Microsoft » Ddue » Tools » ResolveConceptualLinksComponent.cs
// Copyright  Microsoft Corporation.
// This source file is subject to the Microsoft Permissive License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Text.RegularExpressions;
using System.Web;


namespace Microsoft.Ddue.Tools{

  public class ResolveConceptualLinksComponent : BuildComponent {

        // Instantiation logic

    public ResolveConceptualLinksComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) {

            string showBrokenLinkTextValue = configuration.GetAttribute("showBrokenLinkText", String.Empty);
            if (!String.IsNullOrEmpty(showBrokenLinkTextValue)) showBrokenLinkText = Convert.ToBoolean(showBrokenLinkTextValue);

      XPathNodeIterator targetsNodes = configuration.Select("targets");
      foreach (XPathNavigator targetsNode in targetsNodes) {

                // the base directory containing target; required
                string baseValue = targetsNode.GetAttribute("base", String.Empty);
                if (String.IsNullOrEmpty(baseValue)) WriteMessage(MessageLevel.Error, "Every targets element must have a base attribute that specifies the path to a directory of target metadata files.");
                baseValue = Environment.ExpandEnvironmentVariables(baseValue);
                if (!Directory.Exists(baseValue)) WriteMessage(MessageLevel.Error, String.Format("The specified target metadata directory '{0}' does not exist.", baseValue));

                // an xpath expression to construct a file name
                // (not currently used; pattern is hard-coded to $target.cmp.xml
                string filesValue = targetsNode.GetAttribute("files", String.Empty);

                // an xpath expression to construct a url
                string urlValue = targetsNode.GetAttribute("url", String.Empty);
                XPathExpression urlExpression;
                if (String.IsNullOrEmpty(urlValue)) {
                    urlExpression = XPathExpression.Compile("concat(/metadata/topic/@id,'.htm')");
                } else {
                    urlExpression = CompileXPathExpression(urlValue);
                }
                
                // an xpath expression to construct link text
                string textValue = targetsNode.GetAttribute("text", String.Empty);
                XPathExpression textExpression;
                if (String.IsNullOrEmpty(textValue)) {
                    textExpression = XPathExpression.Compile("string(/metadata/topic/title)");
                } else {
                    textExpression = CompileXPathExpression(textValue);
                }

                // the type of link to create to targets found in the directory; required
                string typeValue = targetsNode.GetAttribute("type", String.Empty);
                if (String.IsNullOrEmpty(typeValue)) WriteMessage(MessageLevel.Error, "Every targets element must have a type attribute that specifies what kind of link to create to targets found in that directory.");
                
                // convert the link type to an enumeration member
                LinkType type = LinkType.None;
                try {
                    type = (LinkType) Enum.Parse(typeof(LinkType), typeValue, true);
                } catch (ArgumentException) {
                    WriteMessage(MessageLevel.Error, String.Format("'{0}' is not a valid link type.", typeValue));
                }

                // we have all the required information; create a TargetDirectory and add it to our collection
                TargetDirectory targetDirectory = new TargetDirectory(baseValue, urlExpression, textExpression, type);
                targetDirectories.Add(targetDirectory);

            }

            WriteMessage(MessageLevel.Info, String.Format("Collected {0} targets directories.", targetDirectories.Count));  

    }

        private XPathExpression CompileXPathExpression (string xpath) {
            XPathExpression expression = null;
            try {
                expression = XPathExpression.Compile(xpath);
            } catch (ArgumentException e) {
                WriteMessage(MessageLevel.Error, String.Format("'{0}' is not a valid XPath expression. The error message is: {1}", xpath, e.Message));
            } catch (XPathException e) {
                WriteMessage(MessageLevel.Error, String.Format("'{0}' is not a valid XPath expression. The error message is: {1}", xpath, e.Message));
            }
            return (expression);
        }

        // Conceptual link resolution logic

    public override void Apply (XmlDocument document, string key) {
      ResolveConceptualLinks(document, key);
    }

        private bool showBrokenLinkText = false;

        private string BrokenLinkDisplayText (string target, string text) {
            if (showBrokenLinkText) {
                return(String.Format("{0}", text));
            } else {
                return(String.Format("[{0}]", target));
            }
        }

        private TargetDirectoryCollection targetDirectories = new TargetDirectoryCollection();

        private static XPathExpression conceptualLinks = XPathExpression.Compile("//conceptualLink");

        private static Regex validGuid = new Regex(@"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", RegexOptions.Compiled);

    private void ResolveConceptualLinks (XmlDocument document, string key) {

      // find links
      XPathNodeIterator linkIterator = document.CreateNavigator().Select(conceptualLinks);

      // copy them to an array, because enumerating through an XPathNodeIterator
      // fails when the nodes in it are altered
      XPathNavigator[] linkNodes = BuildComponentUtilities.ConvertNodeIteratorToArray(linkIterator);
      
      foreach (XPathNavigator linkNode in linkNodes) {

                ConceptualLinkInfo link = ConceptualLinkInfo.Create(linkNode);

                // determine url, text, and link type
                string url = null;
                string text = null;
                LinkType type = LinkType.None;
                bool isValidLink = validGuid.IsMatch(link.Target);
                if (isValidLink) {
                    // a valid link; try to fetch target info
                    TargetInfo target = GetTargetInfoFromCache(link.Target.ToLower());
                    if (target == null) {
                        // no target found; issue warning, set link style to none, and text to in-source fall-back
                        //type = LinkType.None;
                        type = LinkType.Index;
                        text = BrokenLinkDisplayText(link.Target, link.Text);
                        WriteMessage(MessageLevel.Warn, String.Format("Unknown conceptual link target '{0}'.", link.Target));
                    } else {
                        // found target; get url, text, and type from stored info
                        url = target.Url;
                        text = target.Text;
                        type = target.Type;
                    }
                } else {
                    // not a valid link; issue warning, set link style to none, and text to invalid target
                    //type = LinkType.None;
                    type = LinkType.Index;
                    text = BrokenLinkDisplayText(link.Target, link.Text);
                    WriteMessage(MessageLevel.Warn, String.Format("Invalid conceptual link target '{0}'.", link.Target));
                }

                // write opening link tag and target info
                XmlWriter writer = linkNode.InsertAfter();
                switch (type) {
                    case LinkType.None:
                        writer.WriteStartElement("span");
                        writer.WriteAttributeString("class", "nolink");
                        break;
                    case LinkType.Local:
                        writer.WriteStartElement("a");
                        writer.WriteAttributeString("href", url);
                        break;
                    case LinkType.Index:
                        writer.WriteStartElement("mshelp", "link", "http://msdn.microsoft.com/mshelp");
                        writer.WriteAttributeString("keywords", link.Target.ToLower());
                        writer.WriteAttributeString("tabindex", "0");
                        break;
                }

                // write the link text
                writer.WriteString(text);

                // write the closing link tag
                writer.WriteEndElement();
                writer.Close();

                // delete the original tag
                linkNode.DeleteSelf();
            }
    }




    // a simple caching system for target names

    private TargetInfo GetTargetInfoFromCache (string target) {

      TargetInfo info;
      if (!cache.TryGetValue(target, out info)) {
        info = targetDirectories.GetTargetInfo(target + ".cmp.xml");

        if (cache.Count >= cacheSize) cache.Clear();

        cache.Add(target, info);
      }

            return(info);

    }

    private static int cacheSize = 1000;

    private Dictionary<string,TargetInfo> cache = new Dictionary<string,TargetInfo>(cacheSize);

        //private CustomContext context = new CustomContext();

  }

  // different types of links

  internal enum LinkType {
    None,    // not active
    Local,    // a href
    Index    // mshelp:link keyword
        //Regex       // regular expression with match/replace
  }

    // a representation of a targets directory, along with all the assoicated expressions used to
    // find target metadat files in it, and extract urls and link text from those files

    internal class TargetDirectory {

        private string directory;

        private XPathExpression fileExpression = XPathExpression.Compile("concat($target,'.cmp.htm')");

        private XPathExpression urlExpression = XPathExpression.Compile("concat(/metadata/topic/@id,'.htm')");

        private XPathExpression textExpression = XPathExpression.Compile("string(/metadata/topic/title)");

        private LinkType type;

        public string Directory {
            get {
                return (directory);
            }
        }

        public XPathExpression UrlExpression {
            get {
                return (urlExpression);
            }
        }

        public XPathExpression TextExpression {
            get {
                return (textExpression);
            }
        }


        public LinkType LinkType {
            get {
                return (type);
            }
        }

        public TargetDirectory (string directory, LinkType type) {
            if (directory == null) throw new ArgumentNullException("directory");
            this.directory = directory;
            this.type = type;
        }

        public TargetDirectory (string directory, XPathExpression urlExpression, XPathExpression textExpression, LinkType type) {
            if (directory == null) throw new ArgumentNullException("directory");
            if (urlExpression == null) throw new ArgumentNullException("urlExpression");
            if (textExpression == null) throw new ArgumentNullException("textExpression");
            this.directory = directory;
            this.urlExpression = urlExpression;
            this.textExpression = textExpression;
            this.type = type;
        }

        private XPathDocument GetDocument (string file) {
            string path = Path.Combine(directory, file);
            if (File.Exists(path)) {
                XPathDocument document = new XPathDocument(path);
                return (document);
            } else {
                return (null);
            }
        }

        public TargetInfo GetTargetInfo (string file) {
            XPathDocument document = GetDocument(file);
            if (document == null) {
                return(null);
            } else {
                XPathNavigator context = document.CreateNavigator();

                string url = context.Evaluate(urlExpression).ToString();
                string text = context.Evaluate(textExpression).ToString();
                TargetInfo info = new TargetInfo(url, text, type);
                return(info);
            }
        }

        public TargetInfo GetTargetInfo (XPathNavigator linkNode, CustomContext context) {

            // compute the metadata file name to open
            XPathExpression localFileExpression = fileExpression.Clone();
            localFileExpression.SetContext(context);
            string file = linkNode.Evaluate(localFileExpression).ToString();
            if (String.IsNullOrEmpty(file)) return (null);

            // load the metadata file
            XPathDocument metadataDocument = GetDocument(file);
            if (metadataDocument == null) return (null);

            // querry the metadata file for the target url and link text
            XPathNavigator metadataNode = metadataDocument.CreateNavigator();
            XPathExpression localUrlExpression = urlExpression.Clone();
            localUrlExpression.SetContext(context);
            string url = metadataNode.Evaluate(localUrlExpression).ToString();
            XPathExpression localTextExpression = textExpression.Clone();
            localTextExpression.SetContext(context);
            string text = metadataNode.Evaluate(localTextExpression).ToString();

            // return this information
            TargetInfo info = new TargetInfo(url, text, type);
            return (info);
        }



    }

    // our collection of targets directories

    internal class TargetDirectoryCollection {

        public TargetDirectoryCollection() {}

        private List<TargetDirectory> targetDirectories = new List<TargetDirectory>();

        public int Count {
            get {
                return(targetDirectories.Count);
            }
        }

        public void Add (TargetDirectory targetDirectory) {
            targetDirectories.Add(targetDirectory);
        }

        public TargetInfo GetTargetInfo (string file) {
            foreach (TargetDirectory targetDirectory in targetDirectories) {
                TargetInfo info = targetDirectory.GetTargetInfo(file);
                if (info != null) return (info);
            }
            return (null);
        }

        public TargetInfo GetTargetInfo (XPathNavigator linkNode, CustomContext context) {
            foreach (TargetDirectory targetDirectory in targetDirectories) {
                TargetInfo info = targetDirectory.GetTargetInfo(linkNode, context);
                if (info != null) return (info);
            }
            return (null);
        }

    }

    // a representation of a resolved target, containing all the information necessary to actually write out the link

    internal class TargetInfo {

        private string url;

        private string text;

        private LinkType type;

        public string Url {
            get {
                return(url);
            }
        }

        public string Text {
            get {
                return(text);
            }
        }

        public LinkType Type {
            get {
                return(type);
            }
        }

        internal TargetInfo (string url, string text, LinkType type) {
            if (url == null) throw new ArgumentNullException("url");
            if (text == null) throw new ArgumentNullException("url");
            this.url = url;
            this.text = text;
            this.type = type;
        }
    }

    // a representation of a conceptual link

    internal class ConceptualLinkInfo {

        private string target;

        private string text;

        private bool showText = false;

        public string Target {
            get {
                return (target);
            }
        }

        public string Text {
            get {
                return (text);
            }
        }

        public bool ShowText {
            get {
                return (showText);
            }
        }

        private ConceptualLinkInfo () { }

        public static ConceptualLinkInfo Create (XPathNavigator node) {
            if (node == null) throw new ArgumentNullException("node");
            
            ConceptualLinkInfo info = new ConceptualLinkInfo();
            
            info.target = node.GetAttribute("target", String.Empty);
            info.text = node.ToString();
            
            return(info);
        }

    }

}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.