diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs index 6d4337824b..28a4fdfee2 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs @@ -39,17 +39,17 @@ namespace ICSharpCode.AvalonEdit.XmlParser public RawObject Parent { get; set; } -// public RawDocument Document { -// get { -// if (this.Parent != null) { -// return this.Parent.Document; -// } else if (this is RawDocument) { -// return (RawDocument)this; -// } else { -// return null; -// } -// } -// } + public RawDocument Document { + get { + if (this.Parent != null) { + return this.Parent.Document; + } else if (this is RawDocument) { + return (RawDocument)this; + } else { + return null; + } + } + } /// Occurs when the value of any local properties changes. Nested changes do not cause the event to occur public event EventHandler LocalDataChanged; @@ -76,6 +76,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser this.ReadCallID = new object(); } + public virtual IEnumerable GetSelfAndAllChildren() + { + return new RawObject[] { this }; + } + public virtual void UpdateDataFrom(RawObject source) { this.ReadCallID = source.ReadCallID; @@ -139,6 +144,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser UpdateChildrenFrom(src.Children); } + public override IEnumerable GetSelfAndAllChildren() + { + return Enumerable.Union( + new RawContainer[] { this }, + this.Children.SelectMany(x => x.GetSelfAndAllChildren()) + ); + } + // The following should be the only methods that are ever // used to modify the children collection @@ -153,23 +166,24 @@ namespace ICSharpCode.AvalonEdit.XmlParser LogDom("Inserting {0} at index {1}", item, index); item.Parent = this; this.Children.Insert(index, item); -// if (this.Document != null) { -// foreach(RawObject obj in GetSeftAndAllNestedObjects()) { -// this.Document.OnObjectAttached(new RawObjectEventArgs() { Object = obj }); -// } -// } + if (this.Document != null) { + foreach(RawObject obj in GetSelfAndAllChildren()) { + this.Document.OnObjectAttached(item); + } + } } protected virtual void RemoveAt(int index) { - LogDom("Removing {0} at index {1}", this.Children[index], index); - this.Children[index].Parent = null; + RawObject removedItem = this.Children[index]; + LogDom("Removing {0} at index {1}", removedItem, index); + removedItem.Parent = null; this.Children.RemoveAt(index); -// if (this.Document != null) { -// foreach(RawObject obj in GetSeftAndAllNestedObjects()) { -// this.Document.OnObjectDettached(new RawObjectEventArgs() { Object = obj }); -// } -// } + if (this.Document != null) { + foreach(RawObject obj in GetSelfAndAllChildren()) { + this.Document.OnObjectDettached(removedItem); + } + } } /// @@ -255,37 +269,43 @@ namespace ICSharpCode.AvalonEdit.XmlParser public class RawDocument: RawContainer { -// public event EventHandler ObjectAttached; -// public event EventHandler ObjectDettached; + public event EventHandler ObjectAttached; + public event EventHandler ObjectDettached; + + internal void OnObjectAttached(RawObject obj) + { + if (ObjectAttached != null) ObjectAttached(this, new RawObjectEventArgs() { Object = obj } ); + } + + internal void OnObjectDettached(RawObject obj) + { + if (ObjectDettached != null) ObjectDettached(this, new RawObjectEventArgs() { Object = obj } ); + } -// internal void OnObjectAttached(RawObjectEventArgs e) -// { -// if (ObjectAttached != null) ObjectAttached(this, e); -// } -// -// internal void OnObjectDettached(RawObjectEventArgs e) -// { -// if (ObjectDettached != null) ObjectDettached(this, e); -// } + XDocument xDoc; - public XDocument CreateXDocument(bool autoUpdate) + public XDocument GetXDocument() { - LogLinq("Creating XDocument"); - XDocument doc = new XDocument(); - doc.AddAnnotation(this); - UpdateXDocument(doc, autoUpdate); - this.Children.CollectionChanged += delegate { UpdateXDocument(doc, autoUpdate); }; - return doc; + if (xDoc == null) { + LogLinq("Creating XDocument"); + xDoc = new XDocument(); + xDoc.AddAnnotation(this); + UpdateXDocumentChildren(true); + this.Children.CollectionChanged += delegate { UpdateXDocumentChildren(false); }; + } + return xDoc; } - void UpdateXDocument(XDocument doc, bool autoUpdate) + void UpdateXDocumentChildren(bool firstUpdate) { + if (!firstUpdate) LogLinq("Updating XDocument Children"); + RawElement root = this.Children.OfType().FirstOrDefault(x => x.StartTag.OpeningBracket == "<"); - if (doc.Root.GetRawObject() != root) { + if (xDoc.Root.GetRawObject() != root) { if (root != null) { - doc.ReplaceNodes(root.CreateXElement(autoUpdate)); + xDoc.ReplaceNodes(root.GetXElement()); } else { - doc.RemoveNodes(); + xDoc.RemoveNodes(); } } } @@ -344,46 +364,48 @@ namespace ICSharpCode.AvalonEdit.XmlParser } } - public XElement CreateXElement(bool autoUpdate) + XElement xElem; + + public XElement GetXElement() { - LogLinq("Creating XElement '{0}'", this.StartTag.Name); - XElement elem = new XElement(EncodeXName(this.StartTag.Name, this.StartTag.Namesapce)); - elem.AddAnnotation(this); - UpdateXElement(elem, autoUpdate); - UpdateXElementAttributes(elem, autoUpdate); - UpdateXElementChildren(elem, autoUpdate); - if (autoUpdate) { - this.StartTag.LocalDataChanged += delegate { UpdateXElement(elem, autoUpdate); }; - this.StartTag.Children.CollectionChanged += delegate { UpdateXElementAttributes(elem, autoUpdate); }; - this.Children.CollectionChanged += delegate { UpdateXElementChildren(elem, autoUpdate); }; + if (xElem == null) { + LogLinq("Creating XElement '{0}'", this.StartTag.Name); + xElem = new XElement(EncodeXName(this.StartTag.Name, this.StartTag.Namesapce)); + xElem.AddAnnotation(this); + UpdateXElement(true); + UpdateXElementAttributes(true); + UpdateXElementChildren(true); + this.StartTag.LocalDataChanged += delegate { UpdateXElement(false); }; + this.StartTag.Children.CollectionChanged += delegate { UpdateXElementAttributes(false); }; + this.Children.CollectionChanged += delegate { UpdateXElementChildren(false); }; } - return elem; + return xElem; } - void UpdateXElement(XElement elem, bool autoUpdate) + void UpdateXElement(bool firstUpdate) { - LogLinq("Updating XElement '{0}'", this.StartTag.Name); - elem.Name = EncodeXName(this.StartTag.Name, this.StartTag.Namesapce); + if (!firstUpdate) LogLinq("Updating XElement '{0}'", this.StartTag.Name); + + xElem.Name = EncodeXName(this.StartTag.Name, this.StartTag.Namesapce); } - internal void UpdateXElementAttributes(XElement elem, bool autoUpdate) + internal void UpdateXElementAttributes(bool firstUpdate) { - List xAttrs = new List(); - foreach(RawAttribute attr in this.StartTag.Children.OfType()) { - XAttribute existing = elem.Attributes().FirstOrDefault(x => x.GetRawObject() == attr); - xAttrs.Add(existing ?? attr.CreateXAttribute(autoUpdate)); - } - elem.ReplaceAttributes(xAttrs.ToArray()); + if (!firstUpdate) LogLinq("Updating XElement Attributes of '{0}'", this.StartTag.Name); + + xElem.ReplaceAttributes(); // Otherwise we get duplicate item exception + xElem.ReplaceAttributes( + this.StartTag.Children.OfType().Select(x => x.GetXAttribute()).ToArray() + ); } - void UpdateXElementChildren(XElement elem, bool autoUpdate) + void UpdateXElementChildren(bool firstUpdate) { - List xElems = new List(); - foreach(RawElement rawElem in this.Children.OfType()) { - XElement existing = (XElement)elem.Nodes().FirstOrDefault(x => x.GetRawObject() == rawElem); - xElems.Add(existing ?? rawElem.CreateXElement(autoUpdate)); - } - elem.ReplaceNodes(xElems); + if (!firstUpdate) LogLinq("Updating XElement Children of '{0}'", this.StartTag.Name); + + xElem.ReplaceNodes( + this.Children.OfType().Select(x => x.GetXElement()).ToArray() + ); } public override string ToString() @@ -417,28 +439,32 @@ namespace ICSharpCode.AvalonEdit.XmlParser } } - public XAttribute CreateXAttribute(bool autoUpdate) + XAttribute xAttr; + + public XAttribute GetXAttribute() { - LogLinq("Creating XAttribute '{0}={1}'", this.Name, this.Value); - XAttribute attr = new XAttribute(EncodeXName(this.Name, this.Namesapce), string.Empty); - attr.AddAnnotation(this); - bool deleted = false; - UpdateXAttribute(attr, autoUpdate, ref deleted); - if (autoUpdate) this.LocalDataChanged += delegate { UpdateXAttribute(attr, autoUpdate, ref deleted); }; - return attr; + if (xAttr == null) { + LogLinq("Creating XAttribute '{0}={1}'", this.Name, this.Value); + xAttr = new XAttribute(EncodeXName(this.Name, this.Namesapce), string.Empty); + xAttr.AddAnnotation(this); + bool deleted = false; + UpdateXAttribute(true, ref deleted); + this.LocalDataChanged += delegate { if (!deleted) UpdateXAttribute(false, ref deleted); }; + } + return xAttr; } - void UpdateXAttribute(XAttribute attr, bool autoUpdate, ref bool deleted) + void UpdateXAttribute(bool firstUpdate, ref bool deleted) { - if (deleted) return; - LogLinq("Updating XAttribute '{0}={1}'", this.Name, this.Value); - if (attr.Name == EncodeXName(this.Name, this.Namesapce)) { - attr.Value = this.Value ?? string.Empty; + if (!firstUpdate) LogLinq("Updating XAttribute '{0}={1}'", this.Name, this.Value); + + if (xAttr.Name == EncodeXName(this.Name, this.Namesapce)) { + xAttr.Value = this.Value ?? string.Empty; } else { - XElement parent = attr.Parent; - attr.Remove(); - deleted = true; - ((RawElement)parent.GetRawObject()).UpdateXElementAttributes(parent, autoUpdate); + xAttr.Remove(); + xAttr = null; + deleted = true; // No longer get events for this instance + ((RawElement)this.Parent.Parent).UpdateXElementAttributes(false); } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs index 496cafe78b..4e604a26f1 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser public XmlParser(TextDocument textDocument) { - this.userLinqDocument = userDocument.CreateXDocument(true); + this.userLinqDocument = userDocument.GetXDocument(); this.textDocument = textDocument; this.textDocument.Changed += delegate(object sender, DocumentChangeEventArgs e) { changesSinceLastParse.Add(e); @@ -50,12 +50,15 @@ namespace ICSharpCode.AvalonEdit.XmlParser end = Math.Max(Math.Min(end, textDocument.TextLength - 1), 0); foreach(RawObject obj in parsedItems.FindOverlappingSegments(start, end - start)) { parsedItems.Remove(obj); - Log("Removed cached item: {0}", obj); + Log("Removed cached item {0}", obj); } } changesSinceLastParse.Clear(); RawDocument parsedDocument = ReadDocument(); + if (parsedDocument.ReadCallID != userDocument.ReadCallID) { + RawObject.LogDom("Updating main DOM tree..."); + } userDocument.UpdateDataFrom(parsedDocument); return userDocument; }