diff options
Diffstat (limited to 'tools/Sandcastle/Source/BuildAssembler/BuildComponents/TaskGrabberComponent.cs')
-rw-r--r-- | tools/Sandcastle/Source/BuildAssembler/BuildComponents/TaskGrabberComponent.cs | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/TaskGrabberComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/TaskGrabberComponent.cs new file mode 100644 index 0000000..81fc811 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/TaskGrabberComponent.cs @@ -0,0 +1,368 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Xml; +using System.Xml.XPath; +using System.IO; + +// still have problems with spaces + +namespace Microsoft.Ddue.Tools { + + public class TaskGrabberComponent : BuildComponent { + + private XmlNamespaceManager nsManager = new XmlNamespaceManager(new NameTable()); + + private XPathExpression valueQuery = null; + private XPathExpression keyQuery = null; + + private string xpathFromConfig = string.Empty; + + private Dictionary<string, List<string>> bKeywordMap = new Dictionary<string, List<string>>(); + + private Dictionary<string, string> index = new Dictionary<string, string>(); + + // what to copy + private List<CopySet> copySets = new List<CopySet>(); + + public TaskGrabberComponent(BuildAssembler assembler, XPathNavigator configuration) + : base(assembler, configuration) + { + XPathNavigator keywordsNode = configuration.SelectSingleNode("keywords"); + if (keywordsNode == null) + return; + + string filespec = keywordsNode.GetAttribute("files", String.Empty); + filespec = Environment.ExpandEnvironmentVariables(filespec); + string keywordXPath = keywordsNode.GetAttribute("keyword", String.Empty); + string topicXPath = keywordsNode.GetAttribute("topic", String.Empty); + if (String.IsNullOrEmpty(keywordXPath) || String.IsNullOrEmpty(topicXPath) || String.IsNullOrEmpty(filespec)) + return; + + xpathFromConfig = keywordXPath; + + string[] keywordFiles = null; + if (File.Exists(filespec)) + { + // we're loading a single file + AddBKeywords(filespec, topicXPath, keywordXPath); + } + else + { + // must be loading a set of files + if (Directory.Exists(filespec)) + { + // if they specified a directory, transform all the files in the directory + keywordFiles = Directory.GetFiles(filespec); + } + else + { + // it's not a file or a directory, maybe it's a path with wildcards + string directoryPath = Path.GetDirectoryName(filespec); + string filePath = Path.GetFileName(filespec); + keywordFiles = Directory.GetFiles(directoryPath, filePath); + } + foreach (string file in keywordFiles) + { + // load targets from each file + AddBKeywords(file, topicXPath, keywordXPath); + } + } + + // set up the context + // put in a default entry for ddue + nsManager.AddNamespace("ddue", "http://ddue.schemas.microsoft.com/authoring/2003/6"); + // look for context nodes, which could override the default + XPathNodeIterator contextNodes = configuration.Select("context"); + foreach (XPathNavigator contextNode in contextNodes) + { + string prefix = contextNode.GetAttribute("prefix", String.Empty); + string uri = contextNode.GetAttribute("name", String.Empty); + nsManager.AddNamespace(prefix, uri); + } + + + // set up the index of source files + XPathNodeIterator sourceNodes = configuration.Select("source"); + foreach (XPathNavigator sourceNode in sourceNodes) + { + string valueXPath = sourceNode.GetAttribute("value", String.Empty); + string keyXPath = sourceNode.GetAttribute("key", String.Empty); + if (String.IsNullOrEmpty(valueXPath) || String.IsNullOrEmpty(keyXPath)) + { + WriteMessage(MessageLevel.Error, "@key and @value must be set in the 'source' node."); + return; + } + + keyQuery = XPathExpression.Compile(keyXPath); + valueQuery = XPathExpression.Compile(valueXPath); + + // search the data directories for entries + XPathNodeIterator dataNodes = sourceNode.Select("data"); + foreach (XPathNavigator dataNode in dataNodes) + { + string dataFiles = dataNode.GetAttribute("files", String.Empty); + dataFiles = Environment.ExpandEnvironmentVariables(dataFiles); + if ((dataFiles == null) || (dataFiles.Length == 0)) throw new ConfigurationErrorsException("When instantiating a CopyFromDirectory component, you must specify a directory path using the files attribute."); + WriteMessage(MessageLevel.Info, String.Format("Searching for files that match '{0}'.", dataFiles)); + int fileCount = ParseDocuments(dataFiles); + WriteMessage(MessageLevel.Info, String.Format("Found {0} files.", fileCount)); + } + WriteMessage(MessageLevel.Info, String.Format("Indexed {0} elements.", index.Count)); + + } + + // get the copy commands + XPathNodeIterator copyNodes = configuration.Select("copy"); + foreach (XPathNavigator copyNode in copyNodes) + { + string sourceXPath = copyNode.GetAttribute("source", String.Empty); + string targetXPath = copyNode.GetAttribute("target", String.Empty); + if (String.IsNullOrEmpty(sourceXPath) || String.IsNullOrEmpty(targetXPath)) + { + WriteMessage(MessageLevel.Error, "@source and @target must be set in the 'copy' node."); + return; + } + + copySets.Add(new CopySet(sourceXPath, targetXPath, nsManager)); + } + + } + + private string currentKey = string.Empty; + + public override void Apply (XmlDocument document, string key) { + currentKey = key; + + XPathNavigator targetDoc = document.CreateNavigator(); + foreach (CopySet copySet in copySets) + { + XPathExpression targetExpression = copySet.GetTargetExpression(targetDoc, key); + + // get the target nodes in the document + XPathNavigator targetNode = targetDoc.SelectSingleNode(targetExpression); + while (targetNode != null) + { + string targetId = targetNode.Value; + int pound = (string.IsNullOrEmpty(targetId)) ? -1 : targetId.IndexOf("#"); + string bkeyword = (pound == -1) ? "" : targetId.Substring(pound + 1); + if (bkeyword == "") + { + WriteMessage(MessageLevel.Warn, string.Format("Invalid id '{0}' in topic '{1}'.", targetId, currentKey)); + // delete this target and get the next target node + targetNode.DeleteSelf(); + targetNode = targetDoc.SelectSingleNode(targetExpression); + continue; + } + + List<string> idList; + if (!bKeywordMap.TryGetValue(bkeyword, out idList)) + { + WriteMessage(MessageLevel.Warn, string.Format("B-keyword not found '{0}' in topic '{1}'.", targetId, currentKey)); + // delete this target and get the next target node + targetNode.DeleteSelf(); + targetNode = targetDoc.SelectSingleNode(targetExpression); + continue; + } + if (idList.Count > 1) + Console.Write(""); + + // create a 'tasks' node to replace the target + XPathNavigator tasksNode = document.CreateElement("tasks").CreateNavigator(); + tasksNode.CreateAttribute(string.Empty, "bkeyword", string.Empty, bkeyword); + foreach (string topicId in idList) + { + //create a task node for this source topic + XPathNavigator taskNode = document.CreateElement("task").CreateNavigator(); + taskNode.CreateAttribute(string.Empty, "topicId", string.Empty, topicId); + + // get the source document for the topic id + string filepath; + if (!index.TryGetValue(topicId, out filepath)) + { + WriteMessage(MessageLevel.Warn, string.Format("No file found for topicId '{0}' for B-keyword '{1}'. Source topic: '{2}'.", topicId, bkeyword, currentKey)); + continue; + } + + XPathNavigator sourceDoc = new XPathDocument(filepath).CreateNavigator(); + XPathNavigator sourceNode = sourceDoc.SelectSingleNode(valueQuery); + + if (sourceNode == null) continue; + XPathNodeIterator sources = sourceNode.Select(copySet.SourceExpression); + + // append the source nodes to the target node + if (sources.Count > 0) + { + foreach (XPathNavigator source in sources) + taskNode.AppendChild(source); + } + tasksNode.AppendChild(taskNode); + } + targetNode.ReplaceSelf(tasksNode); + // get the next target node + targetNode = targetDoc.SelectSingleNode(targetExpression); + } + } + } + + public int ParseDocuments(string wildcardPath) + { + string directoryPart = Path.GetDirectoryName(wildcardPath); + if (String.IsNullOrEmpty(directoryPart)) directoryPart = Environment.CurrentDirectory; + directoryPart = Path.GetFullPath(directoryPart); + string filePart = Path.GetFileName(wildcardPath); + string[] files = Directory.GetFiles(directoryPart, filePart); + foreach (string file in files) + ParseDocument(file); + return (files.Length); + } + + private void ParseDocument(string file) + { + try + { + XPathDocument document = new XPathDocument(file); + + // set context for the xpath expression + valueQuery.SetContext(nsManager); + keyQuery.SetContext(nsManager); + + XPathNodeIterator valueNodes = document.CreateNavigator().Select(valueQuery); + foreach (XPathNavigator valueNode in valueNodes) + { + XPathNavigator keyNode = valueNode.SelectSingleNode(keyQuery); + if (keyNode == null) continue; + string key = keyNode.Value; + + // log multiple occurences of a single id + if (index.ContainsKey(key)) + { + WriteMessage(MessageLevel.Warn, String.Format("Entries for the key '{0}' occur in both '{1}' and '{2}'. The first entry will be used.", key, index[key], file)); + } + else + { + index[key] = file; + } + + } + } + catch (Exception e) + { + WriteMessage(MessageLevel.Error, e.Message); + } + } + + private XPathDocument GetDocument(string identifier) + { + string file = index[identifier]; + XPathDocument document = new XPathDocument(file); + return (document); + } + + private void AddBKeywords(string file, string topicXPath, string keywordXPath) + { + XPathDocument document = new XPathDocument(file); + XPathNodeIterator targetNodes = document.CreateNavigator().Select(topicXPath); + foreach (XPathNavigator targetNode in targetNodes) + { + string topicId = targetNode.GetAttribute("id", string.Empty); + if (string.IsNullOrEmpty(topicId)) + continue; + foreach (XPathNavigator keywordNode in targetNode.Select(keywordXPath)) + { + string keyword = keywordNode.Value; + if (string.IsNullOrEmpty(keyword)) + continue; + AddValueToListDictionary(bKeywordMap, keyword, topicId); + } + } + } + + public static void AddValueToListDictionary<K, V>(Dictionary<K, List<V>> dict, K key, V value) + { + List<V> list; + try + { + if (!dict.TryGetValue(key, out list)) + { + list = new List<V>(); + dict.Add(key, list); + } + list.Add(value); + } + catch (Exception e) + { + Console.WriteLine("Exception adding to dictionary {0}", key); + Console.WriteLine(e); + throw e; + } + } + + } + + internal class CopySet + { + + public CopySet(string sourceXPath, string targetXPath, XmlNamespaceManager nsMgr) + : this(null, sourceXPath, targetXPath, nsMgr) + { + } + + public CopySet(IndexedFileCache cache, string sourceXPath, string targXPath, XmlNamespaceManager nsMgr) + { + this.fileCache = cache; + this.namespaceMgr = nsMgr; + source = XPathExpression.Compile(sourceXPath); + source.SetContext(nsMgr); + if (targXPath.Contains("{0}")) + { + targetXPath = targXPath; + } + else + { + target = XPathExpression.Compile(targXPath); + target.SetContext(nsMgr); + } + } + + private IndexedFileCache fileCache; + public IndexedFileCache FileCache + { + get + { + return (fileCache); + } + } + + private XPathExpression source; + public XPathExpression SourceExpression + { + get + { + return (source); + } + } + + private XmlNamespaceManager namespaceMgr; + + private string targetXPath = string.Empty; + private XPathExpression target = null; + public XPathExpression GetTargetExpression(XPathNavigator targetDoc, string key) + { + if (target == null) + { + XPathExpression keyedTargetExpression = targetDoc.Compile(string.Format(targetXPath, key)); + keyedTargetExpression.SetContext(namespaceMgr); + return keyedTargetExpression; + } + + return target; + } + + + } + + +}
\ No newline at end of file |