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 @@ -15,6 +15,49 @@ namespace ICSharpCode.SharpDevelop.Dom
/// </remarks>
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>
/// A read-only collection that provides change notifications.
/// </summary>

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

@ -70,6 +70,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -70,6 +70,7 @@ namespace ICSharpCode.SharpDevelop.Dom
sealed class SelectManyModelCollection<TSource, TCollection, TResult> : IModelCollection<TResult>
{
readonly ModelCollectionChangedEvent<TResult> collectionChangedEvent;
readonly IModelCollection<TSource> source;
readonly Func<TSource, IModelCollection<TCollection>> collectionSelector;
readonly Func<TSource, TCollection, TResult> resultSelector;
@ -80,10 +81,9 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -80,10 +81,9 @@ namespace ICSharpCode.SharpDevelop.Dom
this.source = source;
this.collectionSelector = collectionSelector;
this.resultSelector = resultSelector;
collectionChangedEvent = new ModelCollectionChangedEvent<TResult>();
}
ModelCollectionChangedEventHandler<TResult> collectionChanged;
public event ModelCollectionChangedEventHandler<TResult> CollectionChanged {
add {
if (value == null)
@ -97,13 +97,13 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -97,13 +97,13 @@ namespace ICSharpCode.SharpDevelop.Dom
inputCollections.Add(inputCollection);
}
}
collectionChanged += value;
collectionChangedEvent.AddHandler(value);
}
remove {
if (collectionChanged == null)
if (!collectionChangedEvent.ContainsHandlers)
return;
collectionChanged -= value;
if (collectionChanged == null) {
collectionChangedEvent.RemoveHandler(value);
if (!collectionChangedEvent.ContainsHandlers) {
source.CollectionChanged -= OnSourceCollectionChanged;
foreach (var inputCollection in inputCollections) {
inputCollection.UnregisterEvent();
@ -115,8 +115,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -115,8 +115,7 @@ namespace ICSharpCode.SharpDevelop.Dom
void OnCollectionChanged(IReadOnlyCollection<TResult> removedItems, IReadOnlyCollection<TResult> addedItems)
{
if (collectionChanged != null)
collectionChanged(removedItems, addedItems);
collectionChangedEvent.Fire(removedItems, 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 @@ -16,6 +16,7 @@ namespace ICSharpCode.SharpDevelop.Dom
/// </summary>
public class SimpleModelCollection<T> : IMutableModelCollection<T>
{
readonly ModelCollectionChangedEvent<T> collectionChangedEvent;
readonly List<T> list;
List<T> addedItems;
List<T> removedItems;
@ -23,11 +24,13 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -23,11 +24,13 @@ namespace ICSharpCode.SharpDevelop.Dom
public SimpleModelCollection()
{
this.list = new List<T>();
collectionChangedEvent = new ModelCollectionChangedEvent<T>();
}
public SimpleModelCollection(IEnumerable<T> items)
{
this.list = new List<T>(items);
collectionChangedEvent = new ModelCollectionChangedEvent<T>();
}
protected void CheckReentrancy()
@ -41,7 +44,15 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -41,7 +44,15 @@ namespace ICSharpCode.SharpDevelop.Dom
}
#region CollectionChanged / BatchUpdate()
public event ModelCollectionChangedEventHandler<T> CollectionChanged;
public event ModelCollectionChangedEventHandler<T> CollectionChanged
{
add {
collectionChangedEvent.AddHandler(value);
}
remove {
collectionChangedEvent.RemoveHandler(value);
}
}
bool isWithinBatchOperation;
bool isRaisingEvent;
@ -64,9 +75,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -64,9 +75,7 @@ namespace ICSharpCode.SharpDevelop.Dom
protected virtual void OnCollectionChanged(IReadOnlyCollection<T> removedItems, IReadOnlyCollection<T> addedItems)
{
var handler = CollectionChanged;
if (handler != null)
handler(removedItems, addedItems);
collectionChangedEvent.Fire(removedItems, addedItems);
}
public virtual IDisposable BatchUpdate()

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

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

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

@ -11,15 +11,25 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -11,15 +11,25 @@ namespace ICSharpCode.SharpDevelop.Dom
{
sealed class NestedTypeDefinitionModelCollection : IModelCollection<ITypeDefinitionModel>
{
readonly ModelCollectionChangedEvent<ITypeDefinitionModel> collectionChangedEvent;
readonly IEntityModelContext context;
List<TypeDefinitionModel> list = new List<TypeDefinitionModel>();
public NestedTypeDefinitionModelCollection(IEntityModelContext 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()
{
@ -107,9 +117,9 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -107,9 +117,9 @@ namespace ICSharpCode.SharpDevelop.Dom
}
}
// 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;
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 @@ -16,6 +16,7 @@ namespace ICSharpCode.SharpDevelop.Dom
/// </summary>
sealed class TopLevelTypeDefinitionModelCollection : ITypeDefinitionModelCollection
{
readonly ModelCollectionChangedEvent<ITypeDefinitionModel> collectionChangedEvent;
readonly IEntityModelContext context;
Dictionary<TopLevelTypeName, TypeDefinitionModel> dict = new Dictionary<TopLevelTypeName, TypeDefinitionModel>();
@ -24,9 +25,18 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -24,9 +25,18 @@ namespace ICSharpCode.SharpDevelop.Dom
if (context == null)
throw new ArgumentNullException("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()
{
@ -122,9 +132,9 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -122,9 +132,9 @@ namespace ICSharpCode.SharpDevelop.Dom
}
}
// 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;
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 @@ -105,12 +105,14 @@ namespace ICSharpCode.SharpDevelop.Dom
#region Members collection
sealed class MemberCollection : IModelCollection<MemberModel>
{
readonly ModelCollectionChangedEvent<MemberModel> collectionChangedEvent;
readonly TypeDefinitionModel parent;
List<List<MemberModel>> lists = new List<List<MemberModel>>();
public MemberCollection(TypeDefinitionModel parent)
{
this.parent = parent;
collectionChangedEvent = new ModelCollectionChangedEvent<MemberModel>();
}
public void InsertPart(int partIndex, IUnresolvedTypeDefinition newPart)
@ -120,16 +122,14 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -120,16 +122,14 @@ namespace ICSharpCode.SharpDevelop.Dom
newItems.Add(new MemberModel(parent.context, newMember) { strongParentCollectionReference = this });
}
lists.Insert(partIndex, newItems);
if (collectionChanged != null)
collectionChanged(EmptyList<MemberModel>.Instance, newItems);
collectionChangedEvent.Fire(EmptyList<MemberModel>.Instance, newItems);
}
public void RemovePart(int partIndex)
{
var oldItems = lists[partIndex];
lists.RemoveAt(partIndex);
if (collectionChanged != null)
collectionChanged(oldItems, EmptyList<MemberModel>.Instance);
collectionChangedEvent.Fire(oldItems, EmptyList<MemberModel>.Instance);
}
public void UpdatePart(int partIndex, IUnresolvedTypeDefinition newPart)
@ -158,7 +158,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -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,
// 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.
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);
var newItems = new MemberModel[endPosNew - startPos];
for (int i = 0; i < newItems.Length; i++) {
@ -166,8 +166,8 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -166,8 +166,8 @@ namespace ICSharpCode.SharpDevelop.Dom
newItems[i].strongParentCollectionReference = this;
}
list.InsertRange(startPos, newItems);
if (collectionChanged != null && (oldItems.Count > 0 || newItems.Length > 0)) {
collectionChanged(oldItems, newItems);
if (collectionChangedEvent.ContainsHandlers && (oldItems.Count > 0 || newItems.Length > 0)) {
collectionChangedEvent.Fire(oldItems, newItems);
}
}
@ -176,18 +176,16 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -176,18 +176,16 @@ namespace ICSharpCode.SharpDevelop.Dom
return memberModel.SymbolKind == newMember.SymbolKind && memberModel.Name == newMember.Name;
}
ModelCollectionChangedEventHandler<MemberModel> collectionChanged;
public event ModelCollectionChangedEventHandler<MemberModel> CollectionChanged {
add {
collectionChanged += value;
collectionChangedEvent.AddHandler(value);
// Set strong reference to collection while there are event listeners
if (collectionChanged != null)
if (collectionChangedEvent.ContainsHandlers)
parent.membersStrongReference = this;
}
remove {
collectionChanged -= value;
if (collectionChanged == null)
collectionChangedEvent.RemoveHandler(value);
if (!collectionChangedEvent.ContainsHandlers)
parent.membersStrongReference = null;
}
}

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

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

Loading…
Cancel
Save