WdxResolveConceptualLinksComponent.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 » WdxResolveConceptualLinksComponent.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{

    /// <summary>
    /// WdxResolveConceptualLinksComponent handles conceptual links where the target is a GUID. All other kinds
    /// of targets are considered invalid. NOTE: This is an experimental version for WebDocs.
    /// </summary>
  public class WdxResolveConceptualLinksComponent : BuildComponent {

        const int MaxTargetCacheSize = 1000;

        TargetSetList targetSets = new TargetSetList();
        XPathExpression baseUrl;
        string invalidLinkFormat = "<span class='nolink'>{1}</span>";
        string brokenLinkFormat = "<a href='http://msdn2.microsoft.com/en-us/library/{0}'>{1}</a>";
        string defaultFormat = "<a href='{0}'>{1}</a>";

    public WdxResolveConceptualLinksComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) {
            //System.Diagnostics.Debugger.Break();

            string av; // temporary attribute values

            // base-url is an xpath expression that is used to lookup the url that relative links need to be
            // relative to. The lookup is done against the current document. This attribute is needed only if 
            // one of the targets uses relative links that are not in the current directory. If not specified,
            // the target uses the url from the meta data unchanged.
            av = configuration.GetAttribute("base-url", String.Empty);
            if (!String.IsNullOrEmpty(av))
                baseUrl = CompileXPathExpression(av);

            // invalid-link-format specifies a string format to be used for invalid (target is not a valid GUID)
            // links. The string formatter is called with parameter {0} set to the target attribute of the link,
            // and parameter {1} set to the tag content from the source document. A reasonable default is used
            // if the value is not specified.
            av = configuration.GetAttribute("invalid-link-format", String.Empty);
            if (!String.IsNullOrEmpty(av))
                invalidLinkFormat = av;

            // broken-link-format specifies a string format to be used for broken links (target GUID lookup
            // failed in all targets). The string formatter is called with parameter {0} set to the target attribute
            // of the link, and parameter {1} set to the tag content from the source document. A reasonable
            // default is used if the value is not specified.
            av = configuration.GetAttribute("broken-link-format", String.Empty);
            if (!String.IsNullOrEmpty(av))
                brokenLinkFormat = av;

            // <targets> specifies a lookup solution for each possible set of link targets. Each target must
            // specify either a lookup file or error condition (invalid-link, broken-link).
            XPathNodeIterator targetsNodes = configuration.Select("targets");
      foreach (XPathNavigator targetsNode in targetsNodes) {

                // lookup-file specifies the meta data file used for looking up URLs and titles. The value will
                // go through environment variable expansion during setup and then through string formatting after
                // computing the url, with parameter {0} set to the link target GUID. This attribute is required.
                string lookupFile = targetsNode.GetAttribute("lookup-file", String.Empty);
                if (string.IsNullOrEmpty(lookupFile))
                    WriteMessage(MessageLevel.Error, "Each target must have a lookup-file attribute.");
                else
                    lookupFile = Environment.ExpandEnvironmentVariables(lookupFile);
                
                // check-file-exists if specified ensures that the link target file exists; if it doesn't exist we
                // take the broken link action.
                string checkFileExists = targetsNode.GetAttribute("check-file-exists", String.Empty);
                if (!String.IsNullOrEmpty(checkFileExists))
                    checkFileExists = Environment.ExpandEnvironmentVariables(checkFileExists);

                // url is an xpath expression that is used to lookup the link url in the meta data file. The default
                // value can be used to lookup the url in .cmp.xml files.
                av = targetsNode.GetAttribute("url", String.Empty);
                XPathExpression url = String.IsNullOrEmpty(av) ? 
                    XPathExpression.Compile("concat(/metadata/topic/@id,'.htm')") :
                    XPathExpression.Compile(av);

                // text is an xpath expression that is used to lookup the link text in the meta data file. The default
                // value can be used to lookup the link text in .cmp.xml files.
                av = targetsNode.GetAttribute("text", string.Empty);
                XPathExpression text = String.IsNullOrEmpty(av) ?
                    XPathExpression.Compile("string(/metadata/topic/title)") :
                    XPathExpression.Compile(av);

                // relative-url determines whether the links from this target set are relative to the current page
                // and need to be adjusted to the base directory.
                av = targetsNode.GetAttribute("relative-url", String.Empty);
                bool relativeUrl = String.IsNullOrEmpty(av) ? false : Convert.ToBoolean(av);;

                // format is a format string that is used to generate the link. Parameter {0} is the url;
                // parameter {1} is the text. The default creates a standard HTML link.
                string format = targetsNode.GetAttribute("format", String.Empty);
                if (String.IsNullOrEmpty(format))
                    format = defaultFormat;
                
                // target looks OK
                targetSets.Add(new TargetSet(lookupFile, checkFileExists, url, text, relativeUrl, format));
            }

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

        public override void Apply(XmlDocument document, string key) {
            // Run through all conceptual nodes, make sure the target is a valid GUID, attempt to resolve the target
            // to a link using one of the TargetSet definitions, and then replace the node with the result. Errors
            // will be dealt with as follows: 1) bad target (GUID) -> output invalidLinkFormat; 2) broken link (cannot
            // resolve target or the URL is empty) -> output brokenLinkFormat; 3) missing text -> just delete the node
            // and don't output anything (presumably there's no point in creating a link that nobody can see). In all
            // three cases we'll log the problem as a warning.
            string docBaseUrl = baseUrl == null ? null : BuildComponentUtilities.EvalXPathExpr(document, baseUrl, "key", key);
            XPathNavigator[] linkNodes = BuildComponentUtilities.ConvertNodeIteratorToArray(document.CreateNavigator().Select(conceptualLinks));
            foreach (XPathNavigator node in linkNodes) {
                string targetGuid = node.GetAttribute("target", String.Empty);
                string url = targetGuid;
                string text = node.ToString();
                string format;
                if (validGuid.IsMatch(url)) {
                    format = brokenLinkFormat;
                    Target t = targetSets.Lookup(targetGuid);
                    if (t == null) {
                        WriteMessage(MessageLevel.Warn, String.Format("Conceptual link not found in target sets; target={0}", targetGuid));
                    }
                    else {
                        if (!String.IsNullOrEmpty(t.Url)) {
                            format = t.TargetSet.Format;
                            url = (docBaseUrl != null && t.TargetSet.RelativeUrl) ? 
                                BuildComponentUtilities.GetRelativePath(t.Url, docBaseUrl) : t.Url;
                            if (!String.IsNullOrEmpty(t.Text))
                                text = t.Text;
                        }
                        else
                            WriteMessage(MessageLevel.Warn, String.Format("Conceptual link found in target set, but meta data does not specify a url; target={0}", targetGuid));
                    }
                }
                else
                    format = invalidLinkFormat;

                if (String.IsNullOrEmpty(text)) {
                    node.DeleteSelf();
                    WriteMessage(MessageLevel.Warn, String.Format("Skipping conceptual link without text; target={0}", url));
                }
                else {
                    node.OuterXml = String.Format(format, url, text);
                }
            }
    }

        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);


        #region HelperFunctions

        public 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);
        }

        #endregion HelperFunctions


        #region DataStructures

        //
        //  Internal data structures to support WdxResolveConceptualLinksComponent
        //

        class TargetSet {
            string lookupFile;
            string checkFileExists;
            XPathExpression url;
            XPathExpression text;

            bool relativeUrl;
            public bool RelativeUrl {
                get { return relativeUrl; }
            }

            string format;
            public string Format {
                get { return format; }
            }

            public TargetSet(string lookupFile, string checkFileExists, XPathExpression url, XPathExpression text, bool relativeUrl, string format) {
                if (lookupFile == null)
                    throw new ArgumentNullException("lookupFile");
                this.lookupFile = lookupFile;

                this.checkFileExists = checkFileExists;

                if (url == null)
                    throw new ArgumentNullException("url");
                this.url = url;

                if (text == null)
                    throw new ArgumentNullException("text");
                this.text = text;

                this.relativeUrl = relativeUrl;

                if (format == null)
                    throw new ArgumentNullException("format");
                this.format = format;
            }

            public Target Lookup(string targetGuid) {
                string lookupFilePathName = String.Format(lookupFile, targetGuid);
                if (File.Exists(lookupFilePathName) &&
                    (checkFileExists == null || File.Exists(String.Format(checkFileExists, targetGuid)))) {
                    XPathDocument document = new XPathDocument(lookupFilePathName);
                    return new Target(this, 
                        (string)document.CreateNavigator().Evaluate(url), 
                        (string)document.CreateNavigator().Evaluate(text));
                }
                return null;
            }
        }

        class TargetSetList {
            List<TargetSet> targetSets = new List<TargetSet>();
            Dictionary<string, Target> targetCache = new Dictionary<string, Target>();

            public TargetSetList() {
            }

            public void Add(TargetSet targetSet) {
                if (targetSet == null)
                    throw new ArgumentNullException("targetSet");
                this.targetSets.Add(targetSet);
            }

            public int Count {
                get { return targetSets.Count; }
            }

            public Target Lookup(string targetGuid) {
                Target t = null;
                if (!targetCache.TryGetValue(targetGuid, out t)) {
                    foreach (TargetSet ts in targetSets) {
                        t = ts.Lookup(targetGuid);
                        if (t != null) {
                            if (targetCache.Count >= MaxTargetCacheSize)
                                targetCache.Clear();
                            targetCache.Add(targetGuid, t);
                            break;
                        }
                    }
                }
                return t;
            }
        }

        class Target {
            TargetSet targetSet;
            public TargetSet TargetSet {
                get { return targetSet; }
            }

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

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

            public Target(TargetSet targetSet, string url, string text) {
                if (targetSet == null)
                    throw new ArgumentNullException("targetSet");
                this.targetSet = targetSet;
                this.url = url;
                this.text = text;
            }
        }

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