aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlContainer.cs
diff options
context:
space:
mode:
authorRoy Ben Shabat <Roy.mail.net@gmail.com>2019-04-09 01:47:48 +0300
committerRoy Ben Shabat <Roy.mail.net@gmail.com>2019-04-09 01:47:48 +0300
commit080f1697e97e13461ec6df4d31c8924d01257a1b (patch)
treeb1fe0285de7bc9bc52e9e2195e66fe022bf8f5b3 /Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlContainer.cs
parent1608e69a417bc5e40a607c3958c4a60f19f66f1a (diff)
downloadTango-080f1697e97e13461ec6df4d31c8924d01257a1b.tar.gz
Tango-080f1697e97e13461ec6df4d31c8924d01257a1b.zip
MERGE
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlContainer.cs')
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlContainer.cs282
1 files changed, 282 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlContainer.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlContainer.cs
new file mode 100644
index 000000000..3cc716de5
--- /dev/null
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlContainer.cs
@@ -0,0 +1,282 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Abstact base class for all types that can contain child nodes
+ /// </summary>
+ public abstract class AXmlContainer: AXmlObject
+ {
+ /// <summary>
+ /// Children of the node. It is read-only.
+ /// Note that is has CollectionChanged event.
+ /// </summary>
+ public AXmlObjectCollection<AXmlObject> Children { get; private set; }
+
+ /// <summary> Create new container </summary>
+ protected AXmlContainer()
+ {
+ this.Children = new AXmlObjectCollection<AXmlObject>();
+ }
+
+ #region Helpper methods
+
+ ObservableCollection<AXmlElement> elements;
+
+ /// <summary> Gets direcly nested elements (non-recursive) </summary>
+ public ObservableCollection<AXmlElement> Elements {
+ get {
+ if (elements == null) {
+ elements = new FilteredCollection<AXmlElement, AXmlObjectCollection<AXmlObject>>(this.Children);
+ }
+ return elements;
+ }
+ }
+
+ internal AXmlObject FirstChild {
+ get {
+ return this.Children[0];
+ }
+ }
+
+ internal AXmlObject LastChild {
+ get {
+ return this.Children[this.Children.Count - 1];
+ }
+ }
+
+ #endregion
+
+ /// <inheritdoc/>
+ public override IEnumerable<AXmlObject> GetSelfAndAllChildren()
+ {
+ return (new AXmlObject[] { this }).Flatten(
+ delegate(AXmlObject i) {
+ AXmlContainer container = i as AXmlContainer;
+ if (container != null)
+ return container.Children;
+ else
+ return null;
+ }
+ );
+ }
+
+ /// <summary>
+ /// Gets a child fully containg the given offset.
+ /// Goes recursively down the tree.
+ /// Specail case if at the end of attribute or text
+ /// </summary>
+ public AXmlObject GetChildAtOffset(int offset)
+ {
+ foreach(AXmlObject child in this.Children) {
+ if ((child is AXmlAttribute || child is AXmlText) && offset == child.EndOffset) return child;
+ if (child.StartOffset < offset && offset < child.EndOffset) {
+ AXmlContainer container = child as AXmlContainer;
+ if (container != null) {
+ return container.GetChildAtOffset(offset);
+ } else {
+ return child;
+ }
+ }
+ }
+ return this; // No childs at offset
+ }
+
+ // Only these four methods should be used to modify the collection
+
+ /// <summary> To be used exlucively by the parser </summary>
+ internal void AddChild(AXmlObject item)
+ {
+ // Childs can be only added to newly parsed items
+ Assert(this.Parent == null, "I have to be new");
+ Assert(item.IsCached, "Added item must be in cache");
+ // Do not set parent pointer
+ this.Children.InsertItemAt(this.Children.Count, item);
+ }
+
+ /// <summary> To be used exlucively by the parser </summary>
+ internal void AddChildren(IEnumerable<AXmlObject> items)
+ {
+ // Childs can be only added to newly parsed items
+ Assert(this.Parent == null, "I have to be new");
+ // Do not set parent pointer
+ this.Children.InsertItemsAt(this.Children.Count, items.ToList());
+ }
+
+ /// <summary>
+ /// To be used exclusively by the children update algorithm.
+ /// Insert child and keep links consistent.
+ /// </summary>
+ void InsertChild(int index, AXmlObject item)
+ {
+ AXmlParser.Log("Inserting {0} at index {1}", item, index);
+
+ Assert(this.Document != null, "Can not insert to dangling object");
+ Assert(item.Parent != this, "Can not own item twice");
+
+ SetParentPointersInTree(item);
+
+ this.Children.InsertItemAt(index, item);
+
+ this.Document.OnObjectInserted(index, item);
+ }
+
+ /// <summary> Recursively fix all parent pointer in a tree </summary>
+ /// <remarks>
+ /// Cache constraint:
+ /// If cached item has parent set, then the whole subtree must be consistent and document set
+ /// </remarks>
+ void SetParentPointersInTree(AXmlObject item)
+ {
+ // All items come from the parser cache
+
+ if (item.Parent == null) {
+ // Dangling object - either a new parser object or removed tree (still cached)
+ item.Parent = this;
+ item.Document = this.Document;
+ AXmlContainer container = item as AXmlContainer;
+ if (container != null) {
+ foreach(AXmlObject child in container.Children) {
+ container.SetParentPointersInTree(child);
+ }
+ }
+ } else if (item.Parent == this) {
+ // If node is attached and then deattached, it will have null parent pointer
+ // but valid subtree - so its children will alredy have correct parent pointer
+ // like in this case
+ // item.DebugCheckConsistency(false);
+ // Rest of the tree is consistent - do not recurse
+ } else {
+ // From cache & parent set => consitent subtree
+ // item.DebugCheckConsistency(false);
+ // The parent (or any futher parents) can not be part of parsed document
+ // becuase otherwise this item would be included twice => safe to change parents
+ // Maintain cache constraint by setting parents to null
+ foreach(AXmlObject ancest in item.GetAncestors().ToList()) {
+ ancest.Parent = null;
+ }
+ item.Parent = this;
+ // Rest of the tree is consistent - do not recurse
+ }
+ }
+
+ /// <summary>
+ /// To be used exclusively by the children update algorithm.
+ /// Remove child, set parent to null and notify the document
+ /// </summary>
+ void RemoveChild(int index)
+ {
+ AXmlObject removed = this.Children[index];
+ AXmlParser.Log("Removing {0} at index {1}", removed, index);
+
+ // Stop tracking if the object can not be used again
+ if (!removed.IsCached)
+ this.Document.Parser.TrackedSegments.RemoveParsedObject(removed);
+
+ // Null parent pointer
+ Assert(removed.Parent == this, "Inconsistent child");
+ removed.Parent = null;
+
+ this.Children.RemoveItemAt(index);
+
+ this.Document.OnObjectRemoved(index, removed);
+ }
+
+ /// <summary> Verify that the subtree is consistent. Only in debug build. </summary>
+ /// <remarks> Parent pointers might be null or pointing somewhere else in parse tree </remarks>
+ internal override void DebugCheckConsistency(bool checkParentPointers)
+ {
+ base.DebugCheckConsistency(checkParentPointers);
+ AXmlObject prevChild = null;
+ int myStartOffset = this.StartOffset;
+ int myEndOffset = this.EndOffset;
+ foreach(AXmlObject child in this.Children) {
+ Assert(child.Length != 0, "Empty child");
+ if (checkParentPointers) {
+ Assert(child.Parent != null, "Null parent reference");
+ Assert(child.Parent == this, "Inccorect parent reference");
+ }
+ if (this.Document != null) {
+ Assert(child.Document != null, "Child has null document");
+ Assert(child.Document == this.Document, "Child is in different document");
+ }
+ if (this.IsCached)
+ Assert(child.IsCached, "Child not in cache");
+ Assert(myStartOffset <= child.StartOffset && child.EndOffset <= myEndOffset, "Child not within parent text range");
+ if (prevChild != null)
+ Assert(prevChild.EndOffset <= child.StartOffset, "Overlaping childs");
+ child.DebugCheckConsistency(checkParentPointers);
+ prevChild = child;
+ }
+ }
+
+ /// <remarks>
+ /// Note the the method is not called recuively.
+ /// Only the helper methods are recursive.
+ /// </remarks>
+ internal void UpdateTreeFrom(AXmlContainer srcContainer)
+ {
+ this.StartOffset = srcContainer.StartOffset; // Force the update
+ this.UpdateDataFrom(srcContainer);
+ RemoveChildrenNotIn(srcContainer.Children);
+ InsertAndUpdateChildrenFrom(srcContainer.Children);
+ }
+
+ void RemoveChildrenNotIn(IList<AXmlObject> srcList)
+ {
+ Dictionary<int, AXmlObject> srcChildren = srcList.ToDictionary(i => i.StartOffset);
+ for(int i = 0; i < this.Children.Count;) {
+ AXmlObject child = this.Children[i];
+ AXmlObject srcChild;
+
+ if (srcChildren.TryGetValue(child.StartOffset, out srcChild) && child.CanUpdateDataFrom(srcChild)) {
+ // Keep only one item with given offset (we might have several due to deletion)
+ srcChildren.Remove(child.StartOffset);
+ // If contaner that needs updating
+ AXmlContainer childAsContainer = child as AXmlContainer;
+ if (childAsContainer != null && child.LastUpdatedFrom != srcChild)
+ childAsContainer.RemoveChildrenNotIn(((AXmlContainer)srcChild).Children);
+ i++;
+ } else {
+ RemoveChild(i);
+ }
+ }
+ }
+
+ void InsertAndUpdateChildrenFrom(IList<AXmlObject> srcList)
+ {
+ for(int i = 0; i < srcList.Count; i++) {
+ // End of our list?
+ if (i == this.Children.Count) {
+ InsertChild(i, srcList[i]);
+ continue;
+ }
+ AXmlObject child = this.Children[i];
+ AXmlObject srcChild = srcList[i];
+
+ if (child.CanUpdateDataFrom(srcChild)) { // includes offset test
+ // Does it need updating?
+ if (child.LastUpdatedFrom != srcChild) {
+ child.UpdateDataFrom(srcChild);
+ AXmlContainer childAsContainer = child as AXmlContainer;
+ if (childAsContainer != null)
+ childAsContainer.InsertAndUpdateChildrenFrom(((AXmlContainer)srcChild).Children);
+ }
+ } else {
+ InsertChild(i, srcChild);
+ }
+ }
+ Assert(this.Children.Count == srcList.Count, "List lengths differ after update");
+ }
+ }
+}