From cbe7c9f20cd7fe67d2db18d14e4944b3c39628b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Mon, 3 Aug 2009 02:23:19 +0000 Subject: [PATCH] XML Parser: Simplified code by introducing RawContainer git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4582 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../XmlParser/RawObjects.cs | 297 +++++++++--------- .../XmlParser/XmlParser.cs | 44 +-- 2 files changed, 169 insertions(+), 172 deletions(-) diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs index 88541dd91e..6d4337824b 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs @@ -100,10 +100,59 @@ namespace ICSharpCode.AvalonEdit.XmlParser System.Diagnostics.Debug.WriteLine("XML Linq: " + format, args); } - internal void OnInserting(RawObject parent, int index) + protected XName EncodeXName(string name, string ns) + { + if (string.IsNullOrEmpty(name)) name = "_"; + name = XmlConvert.EncodeLocalName(name); + + if (ns == null) ns = string.Empty; + ns = XmlConvert.EncodeLocalName(ns); + + return XName.Get(name, ns); + } + } + + public abstract class RawContainer: RawObject + { + /// + /// Children of the node. Can be Elements, Attributes, etc... + /// Please do not modify directly! + /// + public ObservableCollection Children { get; private set; } + + public RawContainer() + { + this.Children = new ObservableCollection(); + } + + public ObservableCollection Helper_Elements { + get { + return new FilteredObservableCollection(this.Children, x => x is RawElement); + } + } + + public override void UpdateDataFrom(RawObject source) { - LogDom("Inserting {0} at index {1}", this, index); - this.Parent = parent; + if (this.ReadCallID == source.ReadCallID) return; + base.UpdateDataFrom(source); + RawContainer src = (RawContainer)source; + UpdateChildrenFrom(src.Children); + } + + // The following should be the only methods that are ever + // used to modify the children collection + + public void AddChild(RawObject item) + { + item.Parent = this; + this.Children.Add(item); + } + + protected virtual void Insert(int index, RawObject item) + { + 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 }); @@ -111,10 +160,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser // } } - internal void OnRemoving(RawObject parent, int index) + protected virtual void RemoveAt(int index) { - LogDom("Removing {0} at index {1}", this, index); - this.Parent = null; + LogDom("Removing {0} at index {1}", this.Children[index], index); + this.Children[index].Parent = null; + this.Children.RemoveAt(index); // if (this.Document != null) { // foreach(RawObject obj in GetSeftAndAllNestedObjects()) { // this.Document.OnObjectDettached(new RawObjectEventArgs() { Object = obj }); @@ -122,31 +172,92 @@ namespace ICSharpCode.AvalonEdit.XmlParser // } } - protected XName EncodeXName(string name, string ns) + /// + /// Copy items from source list over to destination list. + /// Prefer updating items with matching offsets. + /// + public void UpdateChildrenFrom(IList srcList) { - if (string.IsNullOrEmpty(name)) name = "_"; - name = XmlConvert.EncodeLocalName(name); - - if (ns == null) ns = string.Empty; - ns = XmlConvert.EncodeLocalName(ns); + IList dstList = this.Children; - return XName.Get(name, ns); + // Items up to 'i' shall be matching + int i = 0; + // Do not do anything smart with the start tag + if (this is RawElement) { + dstList[0].UpdateDataFrom(srcList[0]); + i++; + } + while(i < srcList.Count) { + // Item is missing - 'i' is invalid index + if (i >= dstList.Count) { + Insert(i, srcList[i]); + i++; continue; + } + RawObject srcItem = srcList[i]; + RawObject dstItem = dstList[i]; + // Matching and updated + if (srcItem.ReadCallID == dstItem.ReadCallID) { + i++; continue; + } + // Offsets and types are matching + if (srcItem.StartOffset == dstItem.StartOffset && + srcItem.GetType() == dstItem.GetType()) + { + dstItem.UpdateDataFrom(srcItem); + i++; continue; + } + // Try to be smart by inserting or removing items + // Dst offset matches with future src + for(int srcItemIndex = i; srcItemIndex < srcList.Count; srcItemIndex++) { + RawObject src = srcList[srcItemIndex]; + if (src.StartOffset == dstItem.StartOffset && src.GetType() == dstItem.GetType()) { + for(int j = i; j < srcItemIndex; j++) { + Insert(j, srcList[j]); + } + i = srcItemIndex; + goto continue2; + } + } + // Scr offset matches with future dst + for(int dstItemIndex = i; dstItemIndex < dstList.Count; dstItemIndex++) { + RawObject dst = dstList[dstItemIndex]; + if (srcItem.StartOffset == dst.StartOffset && srcItem.GetType() == dst.GetType()) { + for(int j = 0; j < dstItemIndex - i; j++) { + RemoveAt(i); + } + goto continue2; + } + } + // No matches found - just update + if (dstItem.GetType() == srcItem.GetType()) { + dstItem.UpdateDataFrom(srcItem); + i++; continue; + } + // Remove whitespace in hope that element update will occur next + if (dstItem is RawText) { + RemoveAt(i); + continue; + } + // Otherwise just add the item + { + Insert(i, srcList[i]); + i++; continue; + } + // Continue for inner loops + continue2:; + } + // Remove extra items + while(dstList.Count > srcList.Count) { + RemoveAt(srcList.Count); + } } } - public class RawDocument: RawObject + public class RawDocument: RawContainer { - public ObservableCollection Children { get; set; } - // public event EventHandler ObjectAttached; // public event EventHandler ObjectDettached; - public ObservableCollection Helper_Elements { - get { - return new FilteredObservableCollection(this.Children, x => x is RawElement); - } - } - // internal void OnObjectAttached(RawObjectEventArgs e) // { // if (ObjectAttached != null) ObjectAttached(this, e); @@ -157,19 +268,6 @@ namespace ICSharpCode.AvalonEdit.XmlParser // if (ObjectDettached != null) ObjectDettached(this, e); // } - public RawDocument() - { - this.Children = new ObservableCollection(); - } - - public override void UpdateDataFrom(RawObject source) - { - if (this.ReadCallID == source.ReadCallID) return; - base.UpdateDataFrom(source); - RawDocument src = (RawDocument)source; - RawUtils.SmartListUpdate(src.Children, this.Children, this); - } - public XDocument CreateXDocument(bool autoUpdate) { LogLinq("Creating XDocument"); @@ -198,19 +296,13 @@ namespace ICSharpCode.AvalonEdit.XmlParser } } - public class RawTag: RawObject + public class RawTag: RawContainer { public string OpeningBracket { get; set; } // "<" or " Attributes { get; set; } public string ClosingBracket { get; set; } // ">" or "/>" for well formed - public RawTag() - { - this.Attributes = new ObservableCollection(); - } - public override void UpdateDataFrom(RawObject source) { if (this.ReadCallID == source.ReadCallID) return; @@ -227,59 +319,42 @@ namespace ICSharpCode.AvalonEdit.XmlParser this.ClosingBracket = src.ClosingBracket; OnLocalDataChanged(); } - RawUtils.SmartListUpdate(src.Attributes, this.Attributes, this); } public override string ToString() { - return string.Format("[{0} '{1}{2}{3}' Attr:{4}]", base.ToString(), this.OpeningBracket, this.Name, this.ClosingBracket, this.Attributes.Count); + return string.Format("[{0} '{1}{2}{3}' Attr:{4}]", base.ToString(), this.OpeningBracket, this.Name, this.ClosingBracket, this.Children.Count); } } - public class RawElement: RawObject + public class RawElement: RawContainer { - public RawTag StartTag { get; set; } - public ObservableCollection Children { get; set; } - public bool HasEndTag { get; set; } - public RawTag EndTag { get; set; } + public RawTag StartTag { + get { + return (RawTag)this.Children[0]; + } + } public ObservableCollection Helper_AttributesAndElements { get { return new MergedObservableCollection( - new FilteredObservableCollection(this.StartTag.Attributes, x => x is RawAttribute), + new FilteredObservableCollection(this.StartTag.Children, x => x is RawAttribute), new FilteredObservableCollection(this.Children, x => x is RawElement) ); } } - public RawElement() - { - this.StartTag = new RawTag() { Parent = this }; - this.Children = new ObservableCollection(); - this.EndTag = new RawTag() { Parent = this }; - } - - public override void UpdateDataFrom(RawObject source) - { - if (this.ReadCallID == source.ReadCallID) return; - base.UpdateDataFrom(source); - RawElement src = (RawElement)source; - this.StartTag.UpdateDataFrom(src.StartTag); - RawUtils.SmartListUpdate(src.Children, this.Children, this); - this.EndTag.UpdateDataFrom(src.EndTag); - } - public XElement CreateXElement(bool autoUpdate) { LogLinq("Creating XElement '{0}'", this.StartTag.Name); - XElement elem = new XElement(EncodeXName(this.StartTag.Name, this.EndTag.Namesapce)); + 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.Attributes.CollectionChanged += delegate { UpdateXElementAttributes(elem, autoUpdate); }; + this.StartTag.Children.CollectionChanged += delegate { UpdateXElementAttributes(elem, autoUpdate); }; this.Children.CollectionChanged += delegate { UpdateXElementChildren(elem, autoUpdate); }; } return elem; @@ -288,13 +363,13 @@ namespace ICSharpCode.AvalonEdit.XmlParser void UpdateXElement(XElement elem, bool autoUpdate) { LogLinq("Updating XElement '{0}'", this.StartTag.Name); - elem.Name = EncodeXName(this.StartTag.Name, this.EndTag.Namesapce); + elem.Name = EncodeXName(this.StartTag.Name, this.StartTag.Namesapce); } internal void UpdateXElementAttributes(XElement elem, bool autoUpdate) { List xAttrs = new List(); - foreach(RawAttribute attr in this.StartTag.Attributes.OfType()) { + foreach(RawAttribute attr in this.StartTag.Children.OfType()) { XAttribute existing = elem.Attributes().FirstOrDefault(x => x.GetRawObject() == attr); xAttrs.Add(existing ?? attr.CreateXAttribute(autoUpdate)); } @@ -313,7 +388,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser public override string ToString() { - return string.Format("[{0} '{1}{2}{3}' Attr:{4} Chld:{5}]", base.ToString(), this.StartTag.OpeningBracket, this.StartTag.Name, this.StartTag.ClosingBracket, this.StartTag.Attributes.Count, this.Children.Count); + return string.Format("[{0} '{1}{2}{3}' Attr:{4} Chld:{5}]", base.ToString(), this.StartTag.OpeningBracket, this.StartTag.Name, this.StartTag.ClosingBracket, this.StartTag.Children.Count, this.Children.Count); } } @@ -417,83 +492,5 @@ namespace ICSharpCode.AvalonEdit.XmlParser if (xObj == null) return null; return xObj.Annotation(); } - - /// - /// Copy items from source list over to destination list. - /// Prefer updating items with matching offsets. - /// - public static void SmartListUpdate(IList srcList, IList dstList, RawObject dstListOwner) - { - // Items up to 'i' shall be matching - for(int i = 0; i < srcList.Count;) { - // Item is missing - 'i' is invalid index - if (i >= dstList.Count) { - srcList[i].OnInserting(dstListOwner, i); - dstList.Insert(i, srcList[i]); - i++; continue; - } - RawObject srcItem = srcList[i]; - RawObject dstItem = dstList[i]; - // Matching and updated - if (srcItem.ReadCallID == dstItem.ReadCallID) { - i++; continue; - } - // Offsets and types are matching - if (srcItem.StartOffset == dstItem.StartOffset && - srcItem.GetType() == dstItem.GetType()) - { - dstItem.UpdateDataFrom(srcItem); - i++; continue; - } - // Try to be smart by inserting or removing items - // Dst offset matches with future src - for(int srcItemIndex = i; srcItemIndex < srcList.Count; srcItemIndex++) { - RawObject src = srcList[srcItemIndex]; - if (src.StartOffset == dstItem.StartOffset && src.GetType() == dstItem.GetType()) { - for(int j = i; j < srcItemIndex; j++) { - srcList[j].OnInserting(dstListOwner, j); - dstList.Insert(j, srcList[j]); - } - i = srcItemIndex; - goto continue2; - } - } - // Scr offset matches with future dst - for(int dstItemIndex = i; dstItemIndex < dstList.Count; dstItemIndex++) { - RawObject dst = dstList[dstItemIndex]; - if (srcItem.StartOffset == dst.StartOffset && srcItem.GetType() == dst.GetType()) { - for(int j = 0; j < dstItemIndex - i; j++) { - dstList[i].OnRemoving(dstListOwner, i); - dstList.RemoveAt(i); - } - goto continue2; - } - } - // No matches found - just update - if (dstItem.GetType() == srcItem.GetType()) { - dstItem.UpdateDataFrom(srcItem); - i++; continue; - } - // Remove whitespace in hope that element update will occur next - if (dstItem is RawText) { - dstList[i].OnRemoving(dstListOwner, i); - dstList.RemoveAt(i); - continue; - } - // Otherwise just add the item - { - srcList[i].OnInserting(dstListOwner, i); - dstList.Insert(i, srcList[i]); - i++; continue; - } - } - // Remove extra items - while(dstList.Count > srcList.Count) { - dstList[srcList.Count].OnRemoving(dstListOwner, srcList.Count); - dstList.RemoveAt(srcList.Count); - } - // Continue for inner loops - continue2:; - } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs index 89f2f46d5e..496cafe78b 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs @@ -192,9 +192,9 @@ namespace ICSharpCode.AvalonEdit.XmlParser if (IsEndOfFile()) { break; } else if (TryPeek('<')) { - doc.Children.Add(ReadElement(doc)); + doc.AddChild(ReadElement()); } else { - doc.Children.Add(ReadCharacterData(doc)); + doc.AddChild(ReadCharacterData()); } } doc.EndOffset = currentLocation; @@ -204,17 +204,18 @@ namespace ICSharpCode.AvalonEdit.XmlParser return doc; } - RawElement ReadElement(RawObject parent) + RawElement ReadElement() { Debug.Assert(HasMoreData() && TryPeek('<')); RawElement element = ReadFromCache(currentLocation); if (element != null) return element; - element = new RawElement() { Parent = parent }; + element = new RawElement(); element.StartOffset = currentLocation; - element.StartTag = ReadTag(element); + // Read start tag + element.AddChild(ReadTag()); // Read content if (element.StartTag.ClosingBracket == ">" && element.StartTag.OpeningBracket != "(currentLocation); if (tag != null) return tag; - tag = new RawTag() { Parent = parent }; + tag = new RawTag(); tag.StartOffset = currentLocation; if (TryRead('<')) { @@ -276,7 +276,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser // Read attributes while(true) { if (IsWhiteSpace() == true) { - tag.Attributes.Add(ReadWhiteSpace(tag)); + tag.AddChild(ReadWhiteSpace()); } if (TryRead('>')) { tag.ClosingBracket = ">"; @@ -296,7 +296,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser } if (TryPeek('<')) break; if (HasMoreData()) { - tag.Attributes.Add(ReadAttribulte(tag)); + tag.AddChild(ReadAttribulte()); continue; } break; @@ -308,14 +308,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser return tag; } - RawText ReadWhiteSpace(RawObject parent) + RawText ReadWhiteSpace() { Debug.Assert(HasMoreData() && IsWhiteSpace() == true); RawText ws = ReadFromCache(currentLocation); if (ws != null) return ws; - ws = new RawText() { Parent = parent }; + ws = new RawText(); ws.StartOffset = currentLocation; int start = currentLocation; @@ -327,14 +327,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser return ws; } - RawAttribute ReadAttribulte(RawObject parent) + RawAttribute ReadAttribulte() { Debug.Assert(HasMoreData()); RawAttribute attr = ReadFromCache(currentLocation); if (attr != null) return attr; - attr = new RawAttribute() { Parent = parent }; + attr = new RawAttribute(); attr.StartOffset = currentLocation; if (HasMoreData()) { @@ -346,10 +346,10 @@ namespace ICSharpCode.AvalonEdit.XmlParser } int checkpoint = currentLocation; attr.EqualsSign = string.Empty; - if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace(attr).Value; + if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace().Value; if (TryRead('=')) { attr.EqualsSign += "="; - if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace(attr).Value; + if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace().Value; if (IsWhiteSpaceOrReserved() == false) { // Read attribute value int start = currentLocation; @@ -375,14 +375,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser return attr; } - RawText ReadCharacterData(RawObject parent) + RawText ReadCharacterData() { Debug.Assert(HasMoreData()); RawText charData = ReadFromCache(currentLocation); if (charData != null) return charData; - charData = new RawText() { Parent = parent }; + charData = new RawText(); charData.StartOffset = currentLocation; int start = currentLocation;