Browse Source

Workaround of Delegate.Combine issues with contra-variance by introducing custom delegate lists for CollectionChanged event of models.

pull/48/head
Andreas Weizel 12 years ago
parent
commit
d5b0a70eb3
  1. 43
      src/Main/Base/Project/Dom/IModelCollection.cs
  2. 15
      src/Main/Base/Project/Dom/ModelCollectionLinq.cs
  3. 17
      src/Main/Base/Project/Dom/SimpleModelCollection.cs
  4. 18
      src/Main/Base/Project/Src/Project/MSBuildConfigurationOrPlatformNameCollection.cs
  5. 16
      src/Main/SharpDevelop/Dom/NestedTypeDefinitionModelCollection.cs
  6. 16
      src/Main/SharpDevelop/Dom/TopLevelTypeDefinitionModelCollection.cs
  7. 24
      src/Main/SharpDevelop/Dom/TypeDefinitionModel.cs
  8. 18
      src/Main/SharpDevelop/Project/Configuration/SolutionConfigurationOrPlatformNameCollection.cs

43
src/Main/Base/Project/Dom/IModelCollection.cs

@ -15,6 +15,49 @@ namespace ICSharpCode.SharpDevelop.Dom
/// </remarks> /// </remarks>
public delegate void ModelCollectionChangedEventHandler<in T>(IReadOnlyCollection<T> removedItems, IReadOnlyCollection<T> addedItems); public delegate void ModelCollectionChangedEventHandler<in T>(IReadOnlyCollection<T> removedItems, IReadOnlyCollection<T> addedItems);
public class ModelCollectionChangedEvent<T>
{
List<ModelCollectionChangedEventHandler<T>> _handlers = new List<ModelCollectionChangedEventHandler<T>>();
// public static ModelCollectionChangedEvent<T> operator+(ModelCollectionChangedEvent<T> eventObject, ModelCollectionChangedEventHandler<T> handler)
// {
// eventObject._handlers.Add(handler);
// return eventObject;
// }
public void AddHandler(ModelCollectionChangedEventHandler<T> handler)
{
_handlers.Add(handler);
}
// public static ModelCollectionChangedEvent<T> operator-(ModelCollectionChangedEvent<T> eventObject, ModelCollectionChangedEventHandler<T> handler)
// {
// eventObject._handlers.Remove(handler);
// return eventObject;
// }
public void RemoveHandler(ModelCollectionChangedEventHandler<T> handler)
{
_handlers.Remove(handler);
}
public void Fire(IReadOnlyCollection<T> removedItems, IReadOnlyCollection<T> addedItems)
{
foreach (var handler in _handlers) {
if (handler != null) {
handler(removedItems, addedItems);
}
}
}
public bool ContainsHandlers
{
get {
return _handlers.Count > 0;
}
}
}
/// <summary> /// <summary>
/// A read-only collection that provides change notifications. /// A read-only collection that provides change notifications.
/// </summary> /// </summary>

15
src/Main/Base/Project/Dom/ModelCollectionLinq.cs

@ -70,6 +70,7 @@ namespace ICSharpCode.SharpDevelop.Dom
sealed class SelectManyModelCollection<TSource, TCollection, TResult> : IModelCollection<TResult> sealed class SelectManyModelCollection<TSource, TCollection, TResult> : IModelCollection<TResult>
{ {
readonly ModelCollectionChangedEvent<TResult> collectionChangedEvent;
readonly IModelCollection<TSource> source; readonly IModelCollection<TSource> source;
readonly Func<TSource, IModelCollection<TCollection>> collectionSelector; readonly Func<TSource, IModelCollection<TCollection>> collectionSelector;
readonly Func<TSource, TCollection, TResult> resultSelector; readonly Func<TSource, TCollection, TResult> resultSelector;
@ -80,10 +81,9 @@ namespace ICSharpCode.SharpDevelop.Dom
this.source = source; this.source = source;
this.collectionSelector = collectionSelector; this.collectionSelector = collectionSelector;
this.resultSelector = resultSelector; this.resultSelector = resultSelector;
collectionChangedEvent = new ModelCollectionChangedEvent<TResult>();
} }
ModelCollectionChangedEventHandler<TResult> collectionChanged;
public event ModelCollectionChangedEventHandler<TResult> CollectionChanged { public event ModelCollectionChangedEventHandler<TResult> CollectionChanged {
add { add {
if (value == null) if (value == null)
@ -97,13 +97,13 @@ namespace ICSharpCode.SharpDevelop.Dom
inputCollections.Add(inputCollection); inputCollections.Add(inputCollection);
} }
} }
collectionChanged += value; collectionChangedEvent.AddHandler(value);
} }
remove { remove {
if (collectionChanged == null) if (!collectionChangedEvent.ContainsHandlers)
return; return;
collectionChanged -= value; collectionChangedEvent.RemoveHandler(value);
if (collectionChanged == null) { if (!collectionChangedEvent.ContainsHandlers) {
source.CollectionChanged -= OnSourceCollectionChanged; source.CollectionChanged -= OnSourceCollectionChanged;
foreach (var inputCollection in inputCollections) { foreach (var inputCollection in inputCollections) {
inputCollection.UnregisterEvent(); inputCollection.UnregisterEvent();
@ -115,8 +115,7 @@ namespace ICSharpCode.SharpDevelop.Dom
void OnCollectionChanged(IReadOnlyCollection<TResult> removedItems, IReadOnlyCollection<TResult> addedItems) void OnCollectionChanged(IReadOnlyCollection<TResult> removedItems, IReadOnlyCollection<TResult> addedItems)
{ {
if (collectionChanged != null) collectionChangedEvent.Fire(removedItems, addedItems);
collectionChanged(removedItems, addedItems);
} }
void OnSourceCollectionChanged(IReadOnlyCollection<TSource> removedItems, IReadOnlyCollection<TSource> addedItems) void OnSourceCollectionChanged(IReadOnlyCollection<TSource> removedItems, IReadOnlyCollection<TSource> addedItems)

17
src/Main/Base/Project/Dom/SimpleModelCollection.cs

@ -16,6 +16,7 @@ namespace ICSharpCode.SharpDevelop.Dom
/// </summary> /// </summary>
public class SimpleModelCollection<T> : IMutableModelCollection<T> public class SimpleModelCollection<T> : IMutableModelCollection<T>
{ {
readonly ModelCollectionChangedEvent<T> collectionChangedEvent;
readonly List<T> list; readonly List<T> list;
List<T> addedItems; List<T> addedItems;
List<T> removedItems; List<T> removedItems;
@ -23,11 +24,13 @@ namespace ICSharpCode.SharpDevelop.Dom
public SimpleModelCollection() public SimpleModelCollection()
{ {
this.list = new List<T>(); this.list = new List<T>();
collectionChangedEvent = new ModelCollectionChangedEvent<T>();
} }
public SimpleModelCollection(IEnumerable<T> items) public SimpleModelCollection(IEnumerable<T> items)
{ {
this.list = new List<T>(items); this.list = new List<T>(items);
collectionChangedEvent = new ModelCollectionChangedEvent<T>();
} }
protected void CheckReentrancy() protected void CheckReentrancy()
@ -41,7 +44,15 @@ namespace ICSharpCode.SharpDevelop.Dom
} }
#region CollectionChanged / BatchUpdate() #region CollectionChanged / BatchUpdate()
public event ModelCollectionChangedEventHandler<T> CollectionChanged; public event ModelCollectionChangedEventHandler<T> CollectionChanged
{
add {
collectionChangedEvent.AddHandler(value);
}
remove {
collectionChangedEvent.RemoveHandler(value);
}
}
bool isWithinBatchOperation; bool isWithinBatchOperation;
bool isRaisingEvent; bool isRaisingEvent;
@ -64,9 +75,7 @@ namespace ICSharpCode.SharpDevelop.Dom
protected virtual void OnCollectionChanged(IReadOnlyCollection<T> removedItems, IReadOnlyCollection<T> addedItems) protected virtual void OnCollectionChanged(IReadOnlyCollection<T> removedItems, IReadOnlyCollection<T> addedItems)
{ {
var handler = CollectionChanged; collectionChangedEvent.Fire(removedItems, addedItems);
if (handler != null)
handler(removedItems, addedItems);
} }
public virtual IDisposable BatchUpdate() public virtual IDisposable BatchUpdate()

18
src/Main/Base/Project/Src/Project/MSBuildConfigurationOrPlatformNameCollection.cs

@ -16,9 +16,8 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary> /// </summary>
class MSBuildConfigurationOrPlatformNameCollection : IConfigurationOrPlatformNameCollection class MSBuildConfigurationOrPlatformNameCollection : IConfigurationOrPlatformNameCollection
{ {
public event ModelCollectionChangedEventHandler<string> CollectionChanged;
volatile IReadOnlyList<string> listSnapshot = EmptyList<string>.Instance; volatile IReadOnlyList<string> listSnapshot = EmptyList<string>.Instance;
readonly ModelCollectionChangedEvent<string> collectionChangedEvent;
readonly MSBuildBasedProject project; readonly MSBuildBasedProject project;
readonly bool isPlatform; readonly bool isPlatform;
@ -26,6 +25,7 @@ namespace ICSharpCode.SharpDevelop.Project
{ {
this.project = project; this.project = project;
this.isPlatform = isPlatform; this.isPlatform = isPlatform;
collectionChangedEvent = new ModelCollectionChangedEvent<string>();
} }
internal void SetContents(IEnumerable<string> updatedItems) internal void SetContents(IEnumerable<string> updatedItems)
@ -33,13 +33,21 @@ namespace ICSharpCode.SharpDevelop.Project
this.listSnapshot = updatedItems.ToArray(); this.listSnapshot = updatedItems.ToArray();
} }
public event ModelCollectionChangedEventHandler<string> CollectionChanged
{
add {
collectionChangedEvent.AddHandler(value);
}
remove {
collectionChangedEvent.RemoveHandler(value);
}
}
internal void OnCollectionChanged(IReadOnlyCollection<string> oldItems, IReadOnlyCollection<string> newItems) internal void OnCollectionChanged(IReadOnlyCollection<string> oldItems, IReadOnlyCollection<string> newItems)
{ {
if (oldItems.SequenceEqual(newItems)) if (oldItems.SequenceEqual(newItems))
return; return;
var eh = CollectionChanged; collectionChangedEvent.Fire(oldItems, newItems);
if (eh != null)
eh(oldItems, newItems);
} }
#region IReadOnlyCollection implementation #region IReadOnlyCollection implementation

16
src/Main/SharpDevelop/Dom/NestedTypeDefinitionModelCollection.cs

@ -11,15 +11,25 @@ namespace ICSharpCode.SharpDevelop.Dom
{ {
sealed class NestedTypeDefinitionModelCollection : IModelCollection<ITypeDefinitionModel> sealed class NestedTypeDefinitionModelCollection : IModelCollection<ITypeDefinitionModel>
{ {
readonly ModelCollectionChangedEvent<ITypeDefinitionModel> collectionChangedEvent;
readonly IEntityModelContext context; readonly IEntityModelContext context;
List<TypeDefinitionModel> list = new List<TypeDefinitionModel>(); List<TypeDefinitionModel> list = new List<TypeDefinitionModel>();
public NestedTypeDefinitionModelCollection(IEntityModelContext context) public NestedTypeDefinitionModelCollection(IEntityModelContext context)
{ {
this.context = context; this.context = context;
collectionChangedEvent = new ModelCollectionChangedEvent<ITypeDefinitionModel>();
} }
public event ModelCollectionChangedEventHandler<ITypeDefinitionModel> CollectionChanged; public event ModelCollectionChangedEventHandler<ITypeDefinitionModel> CollectionChanged
{
add {
collectionChangedEvent.AddHandler(value);
}
remove {
collectionChangedEvent.RemoveHandler(value);
}
}
public IReadOnlyCollection<ITypeDefinitionModel> CreateSnapshot() public IReadOnlyCollection<ITypeDefinitionModel> CreateSnapshot()
{ {
@ -107,9 +117,9 @@ namespace ICSharpCode.SharpDevelop.Dom
} }
} }
// Raise the event if necessary: // Raise the event if necessary:
if (CollectionChanged != null && (oldModels != null || newModels != null)) { if (collectionChangedEvent.ContainsHandlers && (oldModels != null || newModels != null)) {
IReadOnlyCollection<ITypeDefinitionModel> emptyList = EmptyList<ITypeDefinitionModel>.Instance; IReadOnlyCollection<ITypeDefinitionModel> emptyList = EmptyList<ITypeDefinitionModel>.Instance;
CollectionChanged(oldModels ?? emptyList, newModels ?? emptyList); collectionChangedEvent.Fire(oldModels ?? emptyList, newModels ?? emptyList);
} }
} }
} }

16
src/Main/SharpDevelop/Dom/TopLevelTypeDefinitionModelCollection.cs

@ -16,6 +16,7 @@ namespace ICSharpCode.SharpDevelop.Dom
/// </summary> /// </summary>
sealed class TopLevelTypeDefinitionModelCollection : ITypeDefinitionModelCollection sealed class TopLevelTypeDefinitionModelCollection : ITypeDefinitionModelCollection
{ {
readonly ModelCollectionChangedEvent<ITypeDefinitionModel> collectionChangedEvent;
readonly IEntityModelContext context; readonly IEntityModelContext context;
Dictionary<TopLevelTypeName, TypeDefinitionModel> dict = new Dictionary<TopLevelTypeName, TypeDefinitionModel>(); Dictionary<TopLevelTypeName, TypeDefinitionModel> dict = new Dictionary<TopLevelTypeName, TypeDefinitionModel>();
@ -24,9 +25,18 @@ namespace ICSharpCode.SharpDevelop.Dom
if (context == null) if (context == null)
throw new ArgumentNullException("context"); throw new ArgumentNullException("context");
this.context = context; this.context = context;
collectionChangedEvent = new ModelCollectionChangedEvent<ITypeDefinitionModel>();
} }
public event ModelCollectionChangedEventHandler<ITypeDefinitionModel> CollectionChanged; public event ModelCollectionChangedEventHandler<ITypeDefinitionModel> CollectionChanged
{
add {
collectionChangedEvent.AddHandler(value);
}
remove {
collectionChangedEvent.RemoveHandler(value);
}
}
public IReadOnlyCollection<ITypeDefinitionModel> CreateSnapshot() public IReadOnlyCollection<ITypeDefinitionModel> CreateSnapshot()
{ {
@ -122,9 +132,9 @@ namespace ICSharpCode.SharpDevelop.Dom
} }
} }
// Raise the event if necessary: // Raise the event if necessary:
if (CollectionChanged != null && (oldModels != null || newModels != null)) { if (collectionChangedEvent.ContainsHandlers && (oldModels != null || newModels != null)) {
IReadOnlyCollection<ITypeDefinitionModel> emptyList = EmptyList<ITypeDefinitionModel>.Instance; IReadOnlyCollection<ITypeDefinitionModel> emptyList = EmptyList<ITypeDefinitionModel>.Instance;
CollectionChanged(oldModels ?? emptyList, newModels ?? emptyList); collectionChangedEvent.Fire(oldModels ?? emptyList, newModels ?? emptyList);
} }
} }

24
src/Main/SharpDevelop/Dom/TypeDefinitionModel.cs

@ -105,12 +105,14 @@ namespace ICSharpCode.SharpDevelop.Dom
#region Members collection #region Members collection
sealed class MemberCollection : IModelCollection<MemberModel> sealed class MemberCollection : IModelCollection<MemberModel>
{ {
readonly ModelCollectionChangedEvent<MemberModel> collectionChangedEvent;
readonly TypeDefinitionModel parent; readonly TypeDefinitionModel parent;
List<List<MemberModel>> lists = new List<List<MemberModel>>(); List<List<MemberModel>> lists = new List<List<MemberModel>>();
public MemberCollection(TypeDefinitionModel parent) public MemberCollection(TypeDefinitionModel parent)
{ {
this.parent = parent; this.parent = parent;
collectionChangedEvent = new ModelCollectionChangedEvent<MemberModel>();
} }
public void InsertPart(int partIndex, IUnresolvedTypeDefinition newPart) public void InsertPart(int partIndex, IUnresolvedTypeDefinition newPart)
@ -120,16 +122,14 @@ namespace ICSharpCode.SharpDevelop.Dom
newItems.Add(new MemberModel(parent.context, newMember) { strongParentCollectionReference = this }); newItems.Add(new MemberModel(parent.context, newMember) { strongParentCollectionReference = this });
} }
lists.Insert(partIndex, newItems); lists.Insert(partIndex, newItems);
if (collectionChanged != null) collectionChangedEvent.Fire(EmptyList<MemberModel>.Instance, newItems);
collectionChanged(EmptyList<MemberModel>.Instance, newItems);
} }
public void RemovePart(int partIndex) public void RemovePart(int partIndex)
{ {
var oldItems = lists[partIndex]; var oldItems = lists[partIndex];
lists.RemoveAt(partIndex); lists.RemoveAt(partIndex);
if (collectionChanged != null) collectionChangedEvent.Fire(oldItems, EmptyList<MemberModel>.Instance);
collectionChanged(oldItems, EmptyList<MemberModel>.Instance);
} }
public void UpdatePart(int partIndex, IUnresolvedTypeDefinition newPart) public void UpdatePart(int partIndex, IUnresolvedTypeDefinition newPart)
@ -158,7 +158,7 @@ namespace ICSharpCode.SharpDevelop.Dom
// We might try to be clever here and find a LCS so that we only update the members that were actually changed, // We might try to be clever here and find a LCS so that we only update the members that were actually changed,
// or we might consider moving members around (INotifyCollectionChanged supports moves) // or we might consider moving members around (INotifyCollectionChanged supports moves)
// However, the easiest solution by far is to just remove + readd the whole middle portion. // However, the easiest solution by far is to just remove + readd the whole middle portion.
var oldItems = collectionChanged != null ? list.GetRange(startPos, endPosOld - startPos) : null; var oldItems = collectionChangedEvent.ContainsHandlers ? list.GetRange(startPos, endPosOld - startPos) : null;
list.RemoveRange(startPos, endPosOld - startPos); list.RemoveRange(startPos, endPosOld - startPos);
var newItems = new MemberModel[endPosNew - startPos]; var newItems = new MemberModel[endPosNew - startPos];
for (int i = 0; i < newItems.Length; i++) { for (int i = 0; i < newItems.Length; i++) {
@ -166,8 +166,8 @@ namespace ICSharpCode.SharpDevelop.Dom
newItems[i].strongParentCollectionReference = this; newItems[i].strongParentCollectionReference = this;
} }
list.InsertRange(startPos, newItems); list.InsertRange(startPos, newItems);
if (collectionChanged != null && (oldItems.Count > 0 || newItems.Length > 0)) { if (collectionChangedEvent.ContainsHandlers && (oldItems.Count > 0 || newItems.Length > 0)) {
collectionChanged(oldItems, newItems); collectionChangedEvent.Fire(oldItems, newItems);
} }
} }
@ -176,18 +176,16 @@ namespace ICSharpCode.SharpDevelop.Dom
return memberModel.SymbolKind == newMember.SymbolKind && memberModel.Name == newMember.Name; return memberModel.SymbolKind == newMember.SymbolKind && memberModel.Name == newMember.Name;
} }
ModelCollectionChangedEventHandler<MemberModel> collectionChanged;
public event ModelCollectionChangedEventHandler<MemberModel> CollectionChanged { public event ModelCollectionChangedEventHandler<MemberModel> CollectionChanged {
add { add {
collectionChanged += value; collectionChangedEvent.AddHandler(value);
// Set strong reference to collection while there are event listeners // Set strong reference to collection while there are event listeners
if (collectionChanged != null) if (collectionChangedEvent.ContainsHandlers)
parent.membersStrongReference = this; parent.membersStrongReference = this;
} }
remove { remove {
collectionChanged -= value; collectionChangedEvent.RemoveHandler(value);
if (collectionChanged == null) if (!collectionChangedEvent.ContainsHandlers)
parent.membersStrongReference = null; parent.membersStrongReference = null;
} }
} }

18
src/Main/SharpDevelop/Project/Configuration/SolutionConfigurationOrPlatformNameCollection.cs

@ -22,8 +22,7 @@ namespace ICSharpCode.SharpDevelop.Project
/// </remarks> /// </remarks>
class SolutionConfigurationOrPlatformNameCollection : IConfigurationOrPlatformNameCollection class SolutionConfigurationOrPlatformNameCollection : IConfigurationOrPlatformNameCollection
{ {
public event ModelCollectionChangedEventHandler<string> CollectionChanged; readonly ModelCollectionChangedEvent<string> collectionChangedEvent;
readonly List<string> list = new List<string>(); readonly List<string> list = new List<string>();
volatile IReadOnlyList<string> listSnapshot = EmptyList<string>.Instance; volatile IReadOnlyList<string> listSnapshot = EmptyList<string>.Instance;
readonly ISolution solution; readonly ISolution solution;
@ -33,14 +32,23 @@ namespace ICSharpCode.SharpDevelop.Project
{ {
this.solution = solution; this.solution = solution;
this.isPlatform = isPlatform; this.isPlatform = isPlatform;
collectionChangedEvent = new ModelCollectionChangedEvent<string>();
} }
void OnCollectionChanged(IReadOnlyCollection<string> oldItems, IReadOnlyCollection<string> newItems) void OnCollectionChanged(IReadOnlyCollection<string> oldItems, IReadOnlyCollection<string> newItems)
{ {
this.listSnapshot = list.ToArray(); this.listSnapshot = list.ToArray();
var eh = CollectionChanged; collectionChangedEvent.Fire(oldItems, newItems);
if (eh != null) }
eh(oldItems, newItems);
public event ModelCollectionChangedEventHandler<string> CollectionChanged
{
add {
collectionChangedEvent.AddHandler(value);
}
remove {
collectionChangedEvent.RemoveHandler(value);
}
} }
#region IReadOnlyCollection implementation #region IReadOnlyCollection implementation

Loading…
Cancel
Save