// 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.Xml; using System.Xml.XPath; namespace Microsoft.Ddue.Tools { // the storage system internal class IndexedFileCache { public IndexedFileCache(string keyXPath, string valueXPath, int cacheSize) { valueExpression = XPathExpression.Compile(valueXPath); keyExpression = XPathExpression.Compile(keyXPath); _cacheSize = cacheSize; cache = new Dictionary(_cacheSize); lruLinkedList = new LinkedList(); } // search pattern for value nodes to be mapped private XPathExpression valueExpression; // search pattern for the key identifier (relative to the value node) private XPathExpression keyExpression; // an index mapping topic IDs to files private Dictionary idToFileMap = new Dictionary(); 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); // WriteMessage(MessageLevel.Info, String.Format("Found {0} files.", files.Length) ); foreach (string file in files) { ParseDocument(file); } return (files.Length); } private void ParseDocument(string file) { try { XPathDocument document = new XPathDocument(file); XPathNodeIterator valueNodes = document.CreateNavigator().Select(valueExpression); foreach (XPathNavigator valueNode in valueNodes) { XPathNavigator keyNode = valueNode.SelectSingleNode(keyExpression); if (keyNode == null) continue; string key = keyNode.Value; // log multiple occurences of a single id if (idToFileMap.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, idToFileMap[key], file)); } else { idToFileMap[key] = file; } } } catch (XmlException) { // WriteMessage(MessageLevel.Error, e.Message); } } // a simple document caching mechanism private int _cacheSize = 10; private LinkedList lruLinkedList; private Dictionary cache; private IndexedFile GetCachedDocument(string identifier) { string file; if (idToFileMap.TryGetValue(identifier, out file)) { IndexedFile document; if (cache.TryGetValue(file, out document)) { // move the file from its current position to the head of the lru linked list lruLinkedList.Remove(document.ListNode); lruLinkedList.AddFirst(document.ListNode); } else { // not in the cache, so load and index a new source file document = new IndexedFile(file, valueExpression, keyExpression); if (cache.Count >= _cacheSize) { // the cache is full // the last node in the linked list has the path of the next file to remove from the cache if (lruLinkedList.Last != null) { cache.Remove(lruLinkedList.Last.Value); lruLinkedList.RemoveLast(); } } // add the new file to the cache and to the head of the lru linked list cache.Add(file, document); document.ListNode = lruLinkedList.AddFirst(file); } return (document); } else { return (null); } } public XPathNavigator GetContent(string identifier) { // get the document containing the identifier IndexedFile document = GetCachedDocument(identifier); if (document == null) return (null); // select the comment part of the document return document.GetContent(identifier); } public int Count { get { return (idToFileMap.Count); } } } internal class IndexedFile { Dictionary valueIndex = new Dictionary(); public IndexedFile(string filepath, XPathExpression valueExpression, XPathExpression keyExpression) { XPathDocument xpDoc = new XPathDocument(filepath); XPathNodeIterator valueNodes = xpDoc.CreateNavigator().Select(valueExpression); foreach (XPathNavigator valueNode in valueNodes) { XPathNavigator keyNode = valueNode.SelectSingleNode(keyExpression); if (keyNode == null) continue; string key = keyNode.Value; if (!valueIndex.ContainsKey(key)) valueIndex.Add(key, valueNode); } } public XPathNavigator GetContent(string key) { return valueIndex[key]; } private LinkedListNode listNode; public LinkedListNode ListNode { get { return (listNode); } set { listNode = value; } } } }