summaryrefslogtreecommitdiffstats
path: root/tools/Sandcastle/Source/BuildAssembler/BuildComponents/IndexedFileCache.cs
blob: 3fadba3fdfaa895c1acfb00564a521baabade92e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// 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<string, IndexedFile>(_cacheSize);

			lruLinkedList = new LinkedList<string>();
		}

		// 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<string, string> idToFileMap = new Dictionary<string, string>();

		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<String> lruLinkedList;

		private Dictionary<string, IndexedFile> 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<string, XPathNavigator> valueIndex = new Dictionary<string, XPathNavigator>();

		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<string> listNode;
		public LinkedListNode<string> ListNode
		{
			get
			{
				return (listNode);
			}
			set
			{
				listNode = value;
			}
		}

	}

}