PageRenderTime 58ms CodeModel.GetById 15ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 1ms

/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlContainer.cs

http://github.com/icsharpcode/ILSpy
C# | 282 lines | 187 code | 33 blank | 62 comment | 57 complexity | 192ed39cc91239f33ae1079ea3e7bcf6 MD5 | raw file
  1// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
  2// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
  3
  4using System;
  5using System.Collections.Generic;
  6using System.Collections.ObjectModel;
  7using System.Collections.Specialized;
  8using System.Diagnostics;
  9using System.Linq;
 10
 11using ICSharpCode.AvalonEdit.Document;
 12
 13namespace ICSharpCode.AvalonEdit.Xml
 14{
 15	/// <summary>
 16	/// Abstact base class for all types that can contain child nodes
 17	/// </summary>
 18	public abstract class AXmlContainer: AXmlObject
 19	{
 20		/// <summary>
 21		/// Children of the node.  It is read-only.
 22		/// Note that is has CollectionChanged event.
 23		/// </summary>
 24		public AXmlObjectCollection<AXmlObject> Children { get; private set; }
 25		
 26		/// <summary> Create new container </summary>
 27		protected  AXmlContainer()
 28		{
 29			this.Children = new AXmlObjectCollection<AXmlObject>();
 30		}
 31		
 32		#region Helpper methods
 33		
 34		ObservableCollection<AXmlElement> elements;
 35		
 36		/// <summary> Gets direcly nested elements (non-recursive) </summary>
 37		public ObservableCollection<AXmlElement> Elements {
 38			get {
 39				if (elements == null) {
 40					elements = new FilteredCollection<AXmlElement, AXmlObjectCollection<AXmlObject>>(this.Children);
 41				}
 42				return elements;
 43			}
 44		}
 45		
 46		internal AXmlObject FirstChild {
 47			get {
 48				return this.Children[0];
 49			}
 50		}
 51		
 52		internal AXmlObject LastChild {
 53			get {
 54				return this.Children[this.Children.Count - 1];
 55			}
 56		}
 57		
 58		#endregion
 59		
 60		/// <inheritdoc/>
 61		public override IEnumerable<AXmlObject> GetSelfAndAllChildren()
 62		{
 63			return (new AXmlObject[] { this }).Flatten(
 64				delegate(AXmlObject i) {
 65					AXmlContainer container = i as AXmlContainer;
 66					if (container != null)
 67						return container.Children;
 68					else
 69						return null;
 70				}
 71			);
 72		}
 73		
 74		/// <summary>
 75		/// Gets a child fully containg the given offset.
 76		/// Goes recursively down the tree.
 77		/// Specail case if at the end of attribute or text
 78		/// </summary>
 79		public AXmlObject GetChildAtOffset(int offset)
 80		{
 81			foreach(AXmlObject child in this.Children) {
 82				if ((child is AXmlAttribute || child is AXmlText) && offset == child.EndOffset) return child;
 83				if (child.StartOffset < offset && offset < child.EndOffset) {
 84					AXmlContainer container = child as AXmlContainer;
 85					if (container != null) {
 86						return container.GetChildAtOffset(offset);
 87					} else {
 88						return child;
 89					}
 90				}
 91			}
 92			return this; // No childs at offset
 93		}
 94		
 95		// Only these four methods should be used to modify the collection
 96		
 97		/// <summary> To be used exlucively by the parser </summary>
 98		internal void AddChild(AXmlObject item)
 99		{
100			// Childs can be only added to newly parsed items
101			Assert(this.Parent == null, "I have to be new");
102			Assert(item.IsCached, "Added item must be in cache");
103			// Do not set parent pointer
104			this.Children.InsertItemAt(this.Children.Count, item);
105		}
106		
107		/// <summary> To be used exlucively by the parser </summary>
108		internal void AddChildren(IEnumerable<AXmlObject> items)
109		{
110			// Childs can be only added to newly parsed items
111			Assert(this.Parent == null, "I have to be new");
112			// Do not set parent pointer
113			this.Children.InsertItemsAt(this.Children.Count, items.ToList());
114		}
115		
116		/// <summary>
117		/// To be used exclusively by the children update algorithm.
118		/// Insert child and keep links consistent.
119		/// </summary>
120		void InsertChild(int index, AXmlObject item)
121		{
122			AXmlParser.Log("Inserting {0} at index {1}", item, index);
123			
124			Assert(this.Document != null, "Can not insert to dangling object");
125			Assert(item.Parent != this, "Can not own item twice");
126			
127			SetParentPointersInTree(item);
128			
129			this.Children.InsertItemAt(index, item);
130			
131			this.Document.OnObjectInserted(index, item);
132		}
133		
134		/// <summary> Recursively fix all parent pointer in a tree </summary>
135		/// <remarks>
136		/// Cache constraint:
137		///    If cached item has parent set, then the whole subtree must be consistent and document set
138		/// </remarks>
139		void SetParentPointersInTree(AXmlObject item)
140		{
141			// All items come from the parser cache
142			
143			if (item.Parent == null) {
144				// Dangling object - either a new parser object or removed tree (still cached)
145				item.Parent = this;
146				item.Document = this.Document;
147				AXmlContainer container = item as AXmlContainer;
148				if (container != null) {
149					foreach(AXmlObject child in container.Children) {
150						container.SetParentPointersInTree(child);
151					}
152				}
153			} else if (item.Parent == this) {
154				// If node is attached and then deattached, it will have null parent pointer
155				//   but valid subtree - so its children will alredy have correct parent pointer
156				//   like in this case
157				// item.DebugCheckConsistency(false);
158				// Rest of the tree is consistent - do not recurse
159			} else {
160				// From cache & parent set => consitent subtree
161				// item.DebugCheckConsistency(false);
162				// The parent (or any futher parents) can not be part of parsed document
163				//   becuase otherwise this item would be included twice => safe to change parents
164				// Maintain cache constraint by setting parents to null
165				foreach(AXmlObject ancest in item.GetAncestors().ToList()) {
166					ancest.Parent = null;
167				}
168				item.Parent = this;
169				// Rest of the tree is consistent - do not recurse
170			}
171		}
172		
173		/// <summary>
174		/// To be used exclusively by the children update algorithm.
175		/// Remove child, set parent to null and notify the document
176		/// </summary>
177		void RemoveChild(int index)
178		{
179			AXmlObject removed = this.Children[index];
180			AXmlParser.Log("Removing {0} at index {1}", removed, index);
181			
182			// Stop tracking if the object can not be used again
183			if (!removed.IsCached)
184				this.Document.Parser.TrackedSegments.RemoveParsedObject(removed);
185			
186			// Null parent pointer
187			Assert(removed.Parent == this, "Inconsistent child");
188			removed.Parent = null;
189			
190			this.Children.RemoveItemAt(index);
191			
192			this.Document.OnObjectRemoved(index, removed);
193		}
194		
195		/// <summary> Verify that the subtree is consistent.  Only in debug build. </summary>
196		/// <remarks> Parent pointers might be null or pointing somewhere else in parse tree </remarks>
197		internal override void DebugCheckConsistency(bool checkParentPointers)
198		{
199			base.DebugCheckConsistency(checkParentPointers);
200			AXmlObject prevChild = null;
201			int myStartOffset = this.StartOffset;
202			int myEndOffset = this.EndOffset;
203			foreach(AXmlObject child in this.Children) {
204				Assert(child.Length != 0, "Empty child");
205				if (checkParentPointers) {
206					Assert(child.Parent != null, "Null parent reference");
207					Assert(child.Parent == this, "Inccorect parent reference");
208				}
209				if (this.Document != null) {
210					Assert(child.Document != null, "Child has null document");
211					Assert(child.Document == this.Document, "Child is in different document");
212				}
213				if (this.IsCached)
214					Assert(child.IsCached, "Child not in cache");
215				Assert(myStartOffset <= child.StartOffset && child.EndOffset <= myEndOffset, "Child not within parent text range");
216				if (prevChild != null)
217					Assert(prevChild.EndOffset <= child.StartOffset, "Overlaping childs");
218				child.DebugCheckConsistency(checkParentPointers);
219				prevChild = child;
220			}
221		}
222		
223		/// <remarks>
224		/// Note the the method is not called recuively.
225		/// Only the helper methods are recursive.
226		/// </remarks>
227		internal void UpdateTreeFrom(AXmlContainer srcContainer)
228		{
229			this.StartOffset = srcContainer.StartOffset; // Force the update
230			this.UpdateDataFrom(srcContainer);
231			RemoveChildrenNotIn(srcContainer.Children);
232			InsertAndUpdateChildrenFrom(srcContainer.Children);
233		}
234		
235		void RemoveChildrenNotIn(IList<AXmlObject> srcList)
236		{
237			Dictionary<int, AXmlObject> srcChildren = srcList.ToDictionary(i => i.StartOffset);
238			for(int i = 0; i < this.Children.Count;) {
239				AXmlObject child = this.Children[i];
240				AXmlObject srcChild;
241				
242				if (srcChildren.TryGetValue(child.StartOffset, out srcChild) && child.CanUpdateDataFrom(srcChild)) {
243					// Keep only one item with given offset (we might have several due to deletion)
244					srcChildren.Remove(child.StartOffset);
245					// If contaner that needs updating
246					AXmlContainer childAsContainer = child as AXmlContainer;
247					if (childAsContainer != null && child.LastUpdatedFrom != srcChild)
248						childAsContainer.RemoveChildrenNotIn(((AXmlContainer)srcChild).Children);
249					i++;
250				} else {
251					RemoveChild(i);
252				}
253			}
254		}
255		
256		void InsertAndUpdateChildrenFrom(IList<AXmlObject> srcList)
257		{
258			for(int i = 0; i < srcList.Count; i++) {
259				// End of our list?
260				if (i == this.Children.Count) {
261					InsertChild(i, srcList[i]);
262					continue;
263				}
264				AXmlObject child = this.Children[i];
265				AXmlObject srcChild = srcList[i];
266				
267				if (child.CanUpdateDataFrom(srcChild)) { // includes offset test
268					// Does it need updating?
269					if (child.LastUpdatedFrom != srcChild) {
270						child.UpdateDataFrom(srcChild);
271						AXmlContainer childAsContainer = child as AXmlContainer;
272						if (childAsContainer != null)
273							childAsContainer.InsertAndUpdateChildrenFrom(((AXmlContainer)srcChild).Children);
274					}
275				} else {
276					InsertChild(i, srcChild);
277				}
278			}
279			Assert(this.Children.Count == srcList.Count, "List lengths differ after update");
280		}
281	}
282}