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 ""
public string Namesapce { get; set; }
public string Name { get; set; }
- public ObservableCollection 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 != "" &&
@@ -226,16 +227,15 @@ namespace ICSharpCode.AvalonEdit.XmlParser
break;
} else if (TryPeek('<')) {
if (TryPeek("")) break;
- element.Children.Add(ReadElement(element));
+ element.AddChild(ReadElement());
} else {
- element.Children.Add(ReadCharacterData(element));
+ element.AddChild(ReadCharacterData());
}
}
}
- // Read closing tag
+ // Read end tag
if (TryPeek("")) {
- element.HasEndTag = true;
- element.EndTag = ReadTag(element);
+ element.AddChild(ReadTag());
}
element.EndOffset = currentLocation;
@@ -244,14 +244,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser
return element;
}
- RawTag ReadTag(RawObject parent)
+ RawTag ReadTag()
{
Debug.Assert(HasMoreData() && TryPeek('<'));
RawTag tag = ReadFromCache(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;