Browse Source

XML Parser: Joint multiple collection updates together

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4586 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
David Srbecký 17 years ago
parent
commit
8afd82f176
  1. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
  2. 211
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Collections.cs
  3. 112
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/ObservableCollections.cs
  4. 117
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj

@ -304,7 +304,7 @@ @@ -304,7 +304,7 @@
<Compile Include="Utils\ThrowUtil.cs" />
<Compile Include="Utils\Win32.cs" />
<CodeAnalysisDictionary Include="Properties\CodeAnalysisDictionary.xml" />
<Compile Include="XmlParser\ObservableCollections.cs" />
<Compile Include="XmlParser\Collections.cs" />
<Compile Include="XmlParser\RawObjects.cs" />
<Compile Include="XmlParser\XmlParser.cs" />
<Resource Include="themes\RightArrow.cur" />

211
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Collections.cs

@ -0,0 +1,211 @@ @@ -0,0 +1,211 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
// Missing XML comment
#pragma warning disable 1591
namespace ICSharpCode.AvalonEdit.XmlParser
{
/// <summary>
/// Collection that is publicly read-only and has support
/// for adding/removing multiple items at a time.
/// </summary>
public class ChildrenCollection<T>: Collection<T>, INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null) {
CollectionChanged(this, e);
}
}
protected override void ClearItems()
{
throw new NotSupportedException();
}
protected override void InsertItem(int index, T item)
{
throw new NotSupportedException();
}
protected override void RemoveItem(int index)
{
throw new NotSupportedException();
}
protected override void SetItem(int index, T item)
{
throw new NotSupportedException();
}
internal void InsertItems(int index, IList<T> items)
{
for(int i = 0; i < items.Count; i++) {
base.InsertItem(index + i, items[i]);
}
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, (IList)items, index));
}
internal void RemoveItems(int index, int count)
{
List<T> removed = new List<T>();
for(int i = 0; i < count; i++) {
removed.Add(this[index]);
base.RemoveItem(index);
}
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, (IList)removed, index));
}
}
/// <summary>
/// Collection that presents only some items from the wrapped collection
/// </summary>
public class FilteredCollection<C, T>: ObservableCollection<T> where C: INotifyCollectionChanged, IList<T>
{
C source;
Predicate<T> condition;
List<int> srcPtrs = new List<int>(); // Index to the original collection
public FilteredCollection(C source, Predicate<T> condition)
{
this.source = source;
this.condition = condition;
this.source.CollectionChanged += SourceCollectionChanged;
Reset();
}
void Reset()
{
this.Clear();
srcPtrs.Clear();
for(int i = 0; i < source.Count; i++) {
if (condition(source[i])) {
this.Add(source[i]);
srcPtrs.Add(i);
}
}
}
void SourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch(e.Action) {
case NotifyCollectionChangedAction.Add:
// Update pointers
for(int i = 0; i < srcPtrs.Count; i++) {
if (srcPtrs[i] >= e.NewStartingIndex) {
srcPtrs[i] += e.NewItems.Count;
}
}
// Find where to add items
int addIndex = srcPtrs.FindIndex(srcPtr => srcPtr >= e.NewStartingIndex);
if (addIndex == -1) addIndex = this.Count;
// Add items to collection
for(int i = 0; i < e.NewItems.Count; i++) {
if (condition((T)e.NewItems[i])) {
this.InsertItem(addIndex, (T)e.NewItems[i]);
srcPtrs.Insert(addIndex, e.NewStartingIndex + i);
addIndex++;
}
}
break;
case NotifyCollectionChangedAction.Remove:
// Remove the item from our collection
for(int i = 0; i < e.OldItems.Count; i++) {
// Anyone points to the removed item?
int removeIndex = srcPtrs.IndexOf(e.OldStartingIndex + i);
// Remove
if (removeIndex != -1) {
this.RemoveAt(removeIndex);
srcPtrs.RemoveAt(removeIndex);
}
}
// Update pointers
for(int i = 0; i < srcPtrs.Count; i++) {
if (srcPtrs[i] >= e.OldStartingIndex) {
srcPtrs[i] -= e.OldItems.Count;
}
}
break;
case NotifyCollectionChangedAction.Reset:
Reset();
break;
default:
throw new NotSupportedException(e.Action.ToString());
}
}
}
/// <summary>
/// Two collections in sequence
/// </summary>
public class MergedCollection<C, T>: ObservableCollection<T> where C: INotifyCollectionChanged, IList<T>
{
C a;
C b;
public MergedCollection(C a, C b)
{
this.a = a;
this.b = b;
this.a.CollectionChanged += SourceCollectionAChanged;
this.b.CollectionChanged += SourceCollectionBChanged;
Reset();
}
void Reset()
{
this.Clear();
foreach(T item in a) this.Add(item);
foreach(T item in b) this.Add(item);
}
void SourceCollectionAChanged(object sender, NotifyCollectionChangedEventArgs e)
{
SourceCollectionChanged(0, e);
}
void SourceCollectionBChanged(object sender, NotifyCollectionChangedEventArgs e)
{
SourceCollectionChanged(a.Count, e);
}
void SourceCollectionChanged(int collectionStart, NotifyCollectionChangedEventArgs e)
{
switch(e.Action) {
case NotifyCollectionChangedAction.Add:
for (int i = 0; i < e.NewItems.Count; i++) {
this.InsertItem(collectionStart + e.NewStartingIndex + i, (T)e.NewItems[i]);
}
break;
case NotifyCollectionChangedAction.Remove:
for (int i = 0; i < e.OldItems.Count; i++) {
this.RemoveAt(collectionStart + e.OldStartingIndex);
}
break;
case NotifyCollectionChangedAction.Reset:
Reset();
break;
default:
throw new NotSupportedException(e.Action.ToString());
}
}
}
}

112
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/ObservableCollections.cs

@ -1,112 +0,0 @@ @@ -1,112 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
// Missing XML comment
#pragma warning disable 1591
namespace ICSharpCode.AvalonEdit.XmlParser
{
public class FilteredObservableCollection<T>: ObservableCollection<T>
{
ObservableCollection<T> source;
Predicate<T> condition;
List<int> srcPtrs = new List<int>();
public FilteredObservableCollection(ObservableCollection<T> source, Predicate<T> condition)
{
this.source = source;
this.condition = condition;
for(int i = 0; i < source.Count; i++) {
if (condition(source[i])) {
int index = srcPtrs.Count;
this.InsertItem(index, source[i]);
srcPtrs.Insert(index, i);
}
}
this.source.CollectionChanged += new NotifyCollectionChangedEventHandler(FilteredObservableCollection_CollectionChanged);
}
void FilteredObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove) {
// Remove the item from our collection
if (condition((T)e.OldItems[0])) {
int index = srcPtrs.IndexOf(e.OldStartingIndex);
this.RemoveAt(index);
srcPtrs.RemoveAt(index);
}
// Update pointers
for(int i = 0; i < srcPtrs.Count; i++) {
if (srcPtrs[i] > e.OldStartingIndex) {
srcPtrs[i]--;
}
}
}
if (e.Action == NotifyCollectionChangedAction.Add) {
// Update pointers
for(int i = 0; i < srcPtrs.Count; i++) {
if (srcPtrs[i] >= e.NewStartingIndex) {
srcPtrs[i]++;
}
}
// Add item to collection
if (condition((T)e.NewItems[0])) {
int index = srcPtrs.FindIndex(srcPtr => srcPtr >= e.NewStartingIndex);
if (index == -1) index = srcPtrs.Count;
this.InsertItem(index, (T)e.NewItems[0]);
srcPtrs.Insert(index, e.NewStartingIndex);
}
}
}
}
public class MergedObservableCollection<T>: ObservableCollection<T>
{
ObservableCollection<T> a;
ObservableCollection<T> b;
public MergedObservableCollection(ObservableCollection<T> a, ObservableCollection<T> b)
{
this.a = a;
this.b = b;
foreach(T item in a) this.Add(item);
foreach(T item in b) this.Add(item);
this.a.CollectionChanged += new NotifyCollectionChangedEventHandler(MergedObservableCollection_CollectionAChanged);
this.b.CollectionChanged += new NotifyCollectionChangedEventHandler(MergedObservableCollection_CollectionBChanged);
}
void MergedObservableCollection_CollectionAChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove) {
this.RemoveAt(e.OldStartingIndex);
}
if (e.Action == NotifyCollectionChangedAction.Add) {
this.InsertItem(e.NewStartingIndex, (T)e.NewItems[0]);
}
}
void MergedObservableCollection_CollectionBChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove) {
this.RemoveAt(e.OldStartingIndex + a.Count);
}
if (e.Action == NotifyCollectionChangedAction.Add) {
this.InsertItem(e.NewStartingIndex + a.Count, (T)e.NewItems[0]);
}
}
}
}

117
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs

@ -123,16 +123,16 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -123,16 +123,16 @@ namespace ICSharpCode.AvalonEdit.XmlParser
/// Children of the node. Can be Elements, Attributes, etc...
/// Please do not modify directly!
/// </summary>
public ObservableCollection<RawObject> Children { get; private set; }
public ChildrenCollection<RawObject> Children { get; private set; }
public RawContainer()
{
this.Children = new ObservableCollection<RawObject>();
this.Children = new ChildrenCollection<RawObject>();
}
public ObservableCollection<RawObject> Helper_Elements {
get {
return new FilteredObservableCollection<RawObject>(this.Children, x => x is RawElement);
return new FilteredCollection<ChildrenCollection<RawObject>, RawObject>(this.Children, x => x is RawElement);
}
}
@ -158,30 +158,55 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -158,30 +158,55 @@ namespace ICSharpCode.AvalonEdit.XmlParser
public void AddChild(RawObject item)
{
item.Parent = this;
this.Children.Add(item);
this.Children.InsertItems(this.Children.Count, new RawObject[] {item}.ToList());
}
protected virtual void Insert(int index, RawObject item)
/// <summary>
/// Insert children, set parent for them and notify the document
/// </summary>
protected virtual void Insert(int index, IList<RawObject> items)
{
LogDom("Inserting {0} at index {1}", item, index);
item.Parent = this;
this.Children.Insert(index, item);
if (this.Document != null) {
foreach(RawObject obj in GetSelfAndAllChildren()) {
this.Document.OnObjectAttached(item);
if (items.Count == 1) {
LogDom("Inserting {0} at index {1}", items[0], index);
} else {
LogDom("Inserting at index {0}:", index);
foreach(RawObject item in items) LogDom(" {0}", item);
}
foreach(RawObject item in items) item.Parent = this;
this.Children.InsertItems(index, items);
RawDocument document = this.Document;
if (document != null) {
foreach(RawObject item in items) {
foreach(RawObject obj in item.GetSelfAndAllChildren()) {
document.OnObjectAttached(obj);
}
}
}
}
protected virtual void RemoveAt(int index)
/// <summary>
/// Remove children, set parent to null for them and notify the document
/// </summary>
protected virtual void RemoveAt(int index, int count)
{
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 GetSelfAndAllChildren()) {
this.Document.OnObjectDettached(removedItem);
List<RawObject> removed = new List<RawObject>(count);
for(int i = 0; i < count; i++) {
removed.Add(this.Children[index + i]);
}
if (count == 1) {
LogDom("Removing {0} at index {1}", removed[0], index);
} else {
LogDom("Removing at index {0}:", index);
foreach(RawObject item in removed) LogDom(" {0}", item);
}
foreach(RawObject item in removed) item.Parent = null;
this.Children.RemoveItems(index, count);
RawDocument document = this.Document;
if (document != null) {
foreach(RawObject item in removed) {
foreach(RawObject obj in item.GetSelfAndAllChildren()) {
document.OnObjectDettached(obj);
}
}
}
}
@ -204,7 +229,12 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -204,7 +229,12 @@ namespace ICSharpCode.AvalonEdit.XmlParser
while(i < srcList.Count) {
// Item is missing - 'i' is invalid index
if (i >= dstList.Count) {
Insert(i, srcList[i]);
// Add the rest of the items
List<RawObject> itemsToAdd = new List<RawObject>();
for(int j = i; j < srcList.Count; j++) {
itemsToAdd.Add(srcList[j]);
}
Insert(i, itemsToAdd);
i++; continue;
}
RawObject srcItem = srcList[i];
@ -225,9 +255,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -225,9 +255,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser
for(int srcItemIndex = i; srcItemIndex < srcList.Count; srcItemIndex++) {
RawObject src = srcList[srcItemIndex];
if (src.StartOffset == dstItem.StartOffset && src.GetType() == dstItem.GetType()) {
List<RawObject> itemsToAdd = new List<RawObject>();
for(int j = i; j < srcItemIndex; j++) {
Insert(j, srcList[j]);
itemsToAdd.Add(srcList[j]);
}
Insert(i, itemsToAdd);
i = srcItemIndex;
goto continue2;
}
@ -236,9 +268,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -236,9 +268,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
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);
}
RemoveAt(i, dstItemIndex - i);
goto continue2;
}
}
@ -247,22 +277,22 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -247,22 +277,22 @@ namespace ICSharpCode.AvalonEdit.XmlParser
dstItem.UpdateDataFrom(srcItem);
i++; continue;
}
// Remove whitespace in hope that element update will occur next
if (dstItem is RawText) {
RemoveAt(i);
// Remove fluf in hope that element/attribute update will occur next
if (!(dstItem is RawElement) && !(dstItem is RawAttribute)) {
RemoveAt(i, 1);
continue;
}
// Otherwise just add the item
{
Insert(i, srcList[i]);
Insert(i, new RawObject[] {srcList[i]}.ToList());
i++; continue;
}
// Continue for inner loops
continue2:;
}
// Remove extra items
while(dstList.Count > srcList.Count) {
RemoveAt(srcList.Count);
if (dstList.Count > srcList.Count) {
RemoveAt(srcList.Count, dstList.Count - srcList.Count);
}
}
}
@ -357,9 +387,9 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -357,9 +387,9 @@ namespace ICSharpCode.AvalonEdit.XmlParser
public ObservableCollection<RawObject> Helper_AttributesAndElements {
get {
return new MergedObservableCollection<RawObject>(
new FilteredObservableCollection<RawObject>(this.StartTag.Children, x => x is RawAttribute),
new FilteredObservableCollection<RawObject>(this.Children, x => x is RawElement)
return new MergedCollection<ObservableCollection<RawObject>, RawObject>(
new FilteredCollection<ChildrenCollection<RawObject>, RawObject>(this.StartTag.Children, x => x is RawAttribute),
new FilteredCollection<ChildrenCollection<RawObject>, RawObject>(this.Children, x => x is RawElement)
);
}
}
@ -394,9 +424,8 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -394,9 +424,8 @@ namespace ICSharpCode.AvalonEdit.XmlParser
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<RawAttribute>().Select(x => x.GetXAttribute()).ToArray()
);
XAttribute[] attrs = this.StartTag.Children.OfType<RawAttribute>().Select(x => x.GetXAttribute()).Distinct(new AttributeNameComparer()).ToArray();
xElem.ReplaceAttributes(attrs);
}
void UpdateXElementChildren(bool firstUpdate)
@ -408,6 +437,19 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -408,6 +437,19 @@ namespace ICSharpCode.AvalonEdit.XmlParser
);
}
class AttributeNameComparer: IEqualityComparer<XAttribute>
{
public bool Equals(XAttribute x, XAttribute y)
{
return x.Name == y.Name;
}
public int GetHashCode(XAttribute obj)
{
return obj.Name.GetHashCode();
}
}
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.Children.Count, this.Children.Count);
@ -461,7 +503,8 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -461,7 +503,8 @@ namespace ICSharpCode.AvalonEdit.XmlParser
if (xAttr.Name == EncodeXName(this.Name, this.Namesapce)) {
xAttr.Value = this.Value ?? string.Empty;
} else {
xAttr.Remove();
XElement xParent = xAttr.Parent;
if (xAttr.Parent != null) xAttr.Remove(); // Duplicate items are not added
xAttr = null;
deleted = true; // No longer get events for this instance
((RawElement)this.Parent.Parent).UpdateXElementAttributes(false);

Loading…
Cancel
Save