PageRenderTime 64ms CodeModel.GetById 27ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 0ms

/Plugins/Ycm/Plugin/third_party/ycmd/third_party/OmniSharpServer/NRefactory/ICSharpCode.NRefactory.Xml/TagMatchingHeuristics.cs

https://bitbucket.org/WscriChy/vim-configuration
C# | 352 lines | 295 code | 30 blank | 27 comment | 68 complexity | fca1e05c6b6040b9a12e80e4af6450fd MD5 | raw file
  1// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
  2// 
  3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4// software and associated documentation files (the "Software"), to deal in the Software
  5// without restriction, including without limitation the rights to use, copy, modify, merge,
  6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7// to whom the Software is furnished to do so, subject to the following conditions:
  8// 
  9// The above copyright notice and this permission notice shall be included in all copies or
 10// substantial portions of the Software.
 11// 
 12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
 15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 17// DEALINGS IN THE SOFTWARE.
 18
 19using System;
 20using System.Linq;
 21using System.Collections.Generic;
 22using System.Diagnostics;
 23using System.Threading;
 24using ICSharpCode.NRefactory.Editor;
 25using ICSharpCode.NRefactory.Utils;
 26
 27namespace ICSharpCode.NRefactory.Xml
 28{
 29	class TagMatchingHeuristics
 30	{
 31		readonly ITextSource textSource;
 32		
 33		const int MaxConfigurationCount = 30;
 34		
 35		public TagMatchingHeuristics(ITextSource textSource)
 36		{
 37			this.textSource = textSource;
 38		}
 39		
 40		public InternalDocument CreateDocument(List<InternalObject> tagSoup, CancellationToken cancellationToken)
 41		{
 42			var stack = InsertPlaceholderTags(tagSoup, cancellationToken);
 43			InternalDocument doc = new InternalDocument();
 44			var docElements = CreateElements(ref stack);
 45			docElements.Reverse(); // reverse due to stack
 46			doc.NestedObjects = new InternalObject[docElements.Count];
 47			int pos = 0;
 48			for (int i = 0; i < docElements.Count; i++) {
 49				doc.NestedObjects[i] = docElements[i].SetStartRelativeToParent(pos);
 50				pos += doc.NestedObjects[i].Length;
 51			}
 52			doc.Length = pos;
 53			return doc;
 54		}
 55		
 56		#region Heuristic implementation - Inserting place holders into object stream
 57		// Tags used to guide the element creation
 58		static readonly InternalTag StartTagPlaceholder = new InternalTag { OpeningBracket = "<", ClosingBracket = ">" };
 59		static readonly InternalTag EndTagPlaceholder = new InternalTag { OpeningBracket = "</", ClosingBracket = ">" };
 60		
 61		class OpenTagStack
 62		{
 63			readonly OpenTagStack prev;
 64			public readonly string Name;
 65			public readonly int IndentationLevel;
 66			readonly int hashCode;
 67			
 68			public OpenTagStack()
 69			{
 70			}
 71			
 72			private OpenTagStack(OpenTagStack prev, string name, int indentationLevel)
 73			{
 74				this.prev = prev;
 75				this.Name = name;
 76				this.IndentationLevel = indentationLevel;
 77				unchecked {
 78					this.hashCode = prev.hashCode * 27 + (name.GetHashCode() ^ indentationLevel);
 79				}
 80			}
 81			
 82			public bool IsEmpty {
 83				get { return prev == null; }
 84			}
 85			
 86			public OpenTagStack Push(string name, int indentationLevel)
 87			{
 88				return new OpenTagStack(this, name, indentationLevel);
 89			}
 90			
 91			public OpenTagStack Pop()
 92			{
 93				return prev;
 94			}
 95			
 96			public override int GetHashCode()
 97			{
 98				return hashCode;
 99			}
100			
101			public override bool Equals(object obj)
102			{
103				OpenTagStack o = obj as OpenTagStack;
104				if (o != null && hashCode == o.hashCode && IndentationLevel == o.IndentationLevel && Name == o.Name) {
105					if (prev == o.prev)
106						return true;
107					if (prev == null || o.prev == null)
108						return false;
109					return prev.Equals(o.prev);
110				}
111				return false;
112			}
113		}
114		
115		struct Configuration
116		{
117			public readonly OpenTagStack OpenTags;
118			public readonly ImmutableStack<InternalObject> Document;
119			public readonly uint Cost;
120			
121			public Configuration(OpenTagStack openTags, ImmutableStack<InternalObject> document, uint cost)
122			{
123				this.OpenTags = openTags;
124				this.Document = document;
125				this.Cost = cost;
126			}
127		}
128		
129		struct ConfigurationList
130		{
131			internal Configuration[] configurations;
132			internal int count;
133			
134			public static ConfigurationList Create()
135			{
136				return new ConfigurationList {
137					configurations = new Configuration[MaxConfigurationCount],
138					count = 0
139				};
140			}
141			
142			public void Clear()
143			{
144				this.count = 0;
145			}
146			
147			public void Add(OpenTagStack openTags, ImmutableStack<InternalObject> document, uint cost)
148			{
149				Add(new Configuration(openTags, document, cost));
150			}
151			
152			public void Add(Configuration configuration)
153			{
154				for (int i = 0; i < count; i++) {
155					if (configuration.OpenTags.Equals(configurations[i].OpenTags)) {
156						// We found an existing configuration with the same state.
157						// Either replace it, or drop this configurations --
158						// we don't want to add multiple configurations with the same state.
159						if (configuration.Cost < configurations[i].Cost)
160							configurations[i] = configuration;
161						return;
162					}
163				}
164				if (count < configurations.Length) {
165					configurations[count++] = configuration;
166				} else {
167					int index = 0;
168					uint maxCost = configurations[0].Cost;
169					for (int i = 1; i < configurations.Length; i++) {
170						if (configurations[i].Cost < maxCost) {
171							maxCost = configurations[i].Cost;
172							index = i;
173						}
174					}
175					configurations[index] = configuration;
176				}
177			}
178		}
179		
180		const uint InfiniteCost = uint.MaxValue;
181		const uint MissingEndTagCost = 10;
182		const uint IgnoreEndTagCost = 10;
183		const uint MismatchedNameCost = 6;
184		
185		int GetIndentationBefore(int position)
186		{
187			int indentation = 0;
188			while (--position >= 0) {
189				char c = textSource.GetCharAt(position);
190				switch (c) {
191					case ' ':
192						indentation++;
193						break;
194					case '\t':
195						indentation += 4;
196						break;
197					case '\n':
198						return indentation;
199					default:
200						return -1;
201				}
202			}
203			return indentation;
204		}
205		
206		ImmutableStack<InternalObject> InsertPlaceholderTags(List<InternalObject> objects, CancellationToken cancellationToken)
207		{
208			// Calculate indentation levels in front of the tags:
209			int[] indentationBeforeTags = new int[objects.Count];
210			int pos = 0;
211			for (int i = 0; i < objects.Count; i++) {
212				if (objects[i] is InternalTag)
213					indentationBeforeTags[i] = GetIndentationBefore(pos);
214				pos += objects[i].Length;
215			}
216			
217			// Create initial configuration:
218			ConfigurationList listA = ConfigurationList.Create();
219			ConfigurationList listB = ConfigurationList.Create();
220			listA.Add(new Configuration(new OpenTagStack(), ImmutableStack<InternalObject>.Empty, 0));
221			
222			for (int i = 0; i < indentationBeforeTags.Length; i++) {
223				cancellationToken.ThrowIfCancellationRequested();
224				ProcessObject(objects[i], indentationBeforeTags[i], listA, ref listB);
225				Swap(ref listA, ref listB);
226			}
227			
228			Configuration cheapestConfiguration = new Configuration(null, null, InfiniteCost);
229			for (int i = 0; i < listA.count; i++) {
230				Configuration c = listA.configurations[i];
231				if (c.Cost < cheapestConfiguration.Cost) {
232					while (!c.OpenTags.IsEmpty) {
233						c = new Configuration(c.OpenTags.Pop(), c.Document.Push(EndTagPlaceholder), c.Cost + MissingEndTagCost);
234					}
235					if (c.Cost < cheapestConfiguration.Cost)
236						cheapestConfiguration = c;
237				}
238			}
239			Log.WriteLine("Best configuration has cost {0}", cheapestConfiguration.Cost);
240			return cheapestConfiguration.Document;
241		}
242		
243		static void Swap(ref ConfigurationList a, ref ConfigurationList b)
244		{
245			ConfigurationList tmp = a;
246			a = b;
247			b = tmp;
248		}
249		
250		void ProcessObject(InternalObject obj, int indentationLevel, ConfigurationList oldConfigurations, ref ConfigurationList newConfigurations)
251		{
252			newConfigurations.Clear();
253			InternalTag tag = obj as InternalTag;
254			for (int i = 0; i < oldConfigurations.count; i++) {
255				Configuration c = oldConfigurations.configurations[i];
256				if (c.Cost == InfiniteCost)
257					continue;
258				if (tag != null && tag.IsStartTag) {
259					// Push start tag
260					newConfigurations.Add(
261						c.OpenTags.Push(tag.Name, indentationLevel),
262						c.Document.Push(obj),
263						c.Cost
264					);
265				} else if (tag != null && tag.IsEndTag) {
266					// We can ignore this end tag
267					newConfigurations.Add(
268						c.OpenTags,
269						c.Document.Push(StartTagPlaceholder).Push(obj),
270						c.Cost + IgnoreEndTagCost
271					);
272					// We can match this end tag with one of the currently open tags
273					var openTags = c.OpenTags;
274					var documentWithInsertedEndTags = c.Document;
275					uint newCost = c.Cost;
276					while (!openTags.IsEmpty) {
277						uint matchCost = 0;
278						if (openTags.IndentationLevel >= 0 && indentationLevel >= 0)
279							matchCost += (uint)Math.Abs(openTags.IndentationLevel - indentationLevel);
280						if (openTags.Name != tag.Name)
281							matchCost += MismatchedNameCost;
282						newConfigurations.Add(
283							openTags.Pop(),
284							documentWithInsertedEndTags.Push(obj),
285							newCost + matchCost
286						);
287						newCost += MissingEndTagCost;
288						openTags = openTags.Pop();
289						documentWithInsertedEndTags = documentWithInsertedEndTags.Push(EndTagPlaceholder);
290					}
291				} else {
292					newConfigurations.Add(
293						c.OpenTags,
294						c.Document.Push(obj),
295						c.Cost
296					);
297				}
298			}
299		}
300		#endregion
301		
302		#region Create Elements from stack with place holders
303		List<InternalObject> CreateElements(ref ImmutableStack<InternalObject> inputObjects)
304		{
305			List<InternalObject> objects = new List<InternalObject>();
306			while (!inputObjects.IsEmpty) {
307				var obj = inputObjects.Peek();
308				var tag = obj as InternalTag;
309				if (tag != null && tag.IsStartTag)
310					break;
311				inputObjects = inputObjects.Pop();
312				if (tag != null && tag.IsEndTag) {
313					if (inputObjects.Peek() == StartTagPlaceholder) {
314						objects.Add(tag.AddSyntaxError("Matching opening tag was not found"));
315						inputObjects = inputObjects.Pop();
316					} else {
317						var childElements = CreateElements(ref inputObjects);
318						var startTag = (InternalTag)inputObjects.Peek();
319						inputObjects = inputObjects.Pop();
320						childElements.Add(startTag);
321						childElements.Reverse();
322						if (tag != EndTagPlaceholder) {
323							// add end tag
324							if (startTag.Name != tag.Name) {
325								childElements.Add(tag.AddSyntaxError("Expected '</" + startTag.Name + ">'. End tag must have same name as start tag."));
326							} else {
327								childElements.Add(tag);
328							}
329						}
330						InternalElement e = new InternalElement(startTag);
331						e.HasEndTag = (tag != EndTagPlaceholder);
332						e.NestedObjects = new InternalObject[childElements.Count];
333						int pos = 0;
334						for (int i = 0; i < childElements.Count; i++) {
335							e.NestedObjects[i] = childElements[i].SetStartRelativeToParent(pos);
336							pos += e.NestedObjects[i].Length;
337						}
338						e.Length = pos;
339						if (tag == EndTagPlaceholder) {
340							e.SyntaxErrors = new [] { new InternalSyntaxError(pos, pos, "Missing '</" + startTag.Name + ">'") };
341						}
342						objects.Add(e);
343					}
344				} else {
345					objects.Add(obj);
346				}
347			}
348			return objects;
349		}
350		#endregion
351	}
352}