diff --git a/src/ObservableCollections/IObservableCollection.cs b/src/ObservableCollections/IObservableCollection.cs index 1511cc0..d382fc8 100644 --- a/src/ObservableCollections/IObservableCollection.cs +++ b/src/ObservableCollections/IObservableCollection.cs @@ -1,6 +1,4 @@ using ObservableCollections.Internal; -using System; -using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; @@ -39,9 +37,10 @@ namespace ObservableCollections void Sort(IComparer viewComparer); } - public interface IGroupedSynchoronizedView : ILookup, ISynchronizedView - { - } + // will be implemented in the future? + //public interface IGroupedSynchoronizedView : ILookup, ISynchronizedView + //{ + //} public interface INotifyCollectionChangedSynchronizedView : ISynchronizedView, INotifyCollectionChanged, INotifyPropertyChanged { diff --git a/src/ObservableCollections/Internal/GroupedView.cs b/src/ObservableCollections/Internal/GroupedView.cs deleted file mode 100644 index 6b3d065..0000000 --- a/src/ObservableCollections/Internal/GroupedView.cs +++ /dev/null @@ -1,387 +0,0 @@ -using System.Collections; -using System.Collections.Specialized; - -namespace ObservableCollections.Internal -{ - // mutable lookup. - internal class Lookup : ILookup - where TKey : notnull - { - Grouping?[] groupingBuckets; - IEqualityComparer keyComparer; - int count; - Grouping? lastGroup; - - // TODO: - public int Count => throw new NotImplementedException(); - - public int ItemsCount { get; private set; } - - public IEnumerable this[TKey key] => throw new NotImplementedException(); - - public Lookup(IEqualityComparer keyComparer) - { - this.groupingBuckets = new Grouping?[7]; // initial size - this.count = 0; - this.lastGroup = null; - this.keyComparer = keyComparer; - } - - // TODO: - public bool Contains(TKey key) - { - throw new NotImplementedException(); - } - - public void Add(TKey key, TValue value) - { - var keyHash = keyComparer.GetHashCode(key); - var g = groupingBuckets[keyHash % groupingBuckets.Length]; - var last = g; - while (g != null) - { - if (keyComparer.Equals(key, g.key)) - { - break; // hit. - } - - last = g; - g = g.hashNext; - } - - if (g == null) - { - g = new Grouping(key, keyHash); - if (last != null) - { - last.hashNext = g; - } - else - { - if (groupingBuckets.Length == count) - { - Resize(); - } - - groupingBuckets[keyHash % groupingBuckets.Length] = g; - } - count++; // new group added - } - - g.Add(value); - - if (lastGroup == null) - { - lastGroup = g; - lastGroup.nextGroup = g; // last's next is first. - } - else - { - g.nextGroup = lastGroup.nextGroup; - lastGroup.nextGroup = g; - lastGroup = g; - } - - ItemsCount++; - } - - public void RemoveKeyAll(TKey key) - { - throw new NotImplementedException(); - } - - public void RemoveValue(TKey key, TValue value) - { - } - - void Resize() - { - var newSize = checked((count * 2) + 1); - var newGrouping = new Grouping[newSize]; - - var g = lastGroup!; // when resize called, always lastGroup is not null. - do - { - g = g.nextGroup!; // nextGroup is always not null, initial last.next is first. - var index = g.hash % newSize; - g.hashNext = newGrouping[index]; - newGrouping[index] = g; - } - while (g != lastGroup); - - groupingBuckets = newGrouping; - } - - public IEnumerator> GetEnumerator() - { - if (lastGroup == null) yield break; - - var g = lastGroup.nextGroup; // last's next is first. - do - { - if (g == null) yield break; - yield return g; - g = g.nextGroup; - } - while (g != lastGroup); // reaches end. - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - internal class Grouping : IGrouping - { - internal readonly TKey key; - internal readonly int hash; - internal readonly List elements; - - internal Grouping? hashNext; // same buckets linknode - internal Grouping? nextGroup; // guarantee added order - - public TKey Key => key; - - public Grouping(TKey key, int hash) - { - this.key = key; - this.hash = hash; - this.elements = new List(1); // initial size is single. - } - - public void Add(TValue value) - { - elements.Add(value); - } - - public void Remove(TValue value) - { - elements.Remove(value); - } - - public IEnumerator GetEnumerator() - { - return elements.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - internal class GroupedView : IGroupedSynchoronizedView - where TKey : notnull - { - readonly Lookup lookup; - readonly IObservableCollection source; - readonly Func keySelector; - readonly Func viewSelector; - - ISynchronizedViewFilter filter; - - public GroupedView(IObservableCollection source, Func keySelector, Func viewSelector, IEqualityComparer keyComparer) - { - this.source = source; - this.filter = SynchronizedViewFilter.Null; - this.keySelector = keySelector; - this.viewSelector = viewSelector; - - lock (source.SyncRoot) - { - lookup = new Lookup(keyComparer); - - foreach (var value in source) - { - var key = keySelector(value); - var view = viewSelector(value); - lookup.Add(key, (value, view)); - } - - source.CollectionChanged += SourceCollectionChanged; - } - } - - public IEnumerable<(T, TView)> this[TKey key] - { - get - { - lock (SyncRoot) - { - var v = lookup[key]; - foreach (var item in v) - { - yield return item; - } - } - } - } - - public object SyncRoot { get; } = new object(); - - public int Count - { - get - { - lock (SyncRoot) - { - return lookup.Count; - } - } - } - - int IReadOnlyCollection<(T Value, TView View)>.Count - { - get - { - lock (SyncRoot) - { - return lookup.ItemsCount; - } - } - } - - public event NotifyCollectionChangedEventHandler? RoutingCollectionChanged; - public event Action? CollectionStateChanged; - - public void AttachFilter(ISynchronizedViewFilter filter) - { - lock (SyncRoot) - { - this.filter = filter; - foreach (var item in lookup) - { - foreach (var (value, view) in item) - { - filter.InvokeOnAttach(value, view); - } - } - } - } - - public void ResetFilter(Action? resetAction) - { - lock (SyncRoot) - { - this.filter = SynchronizedViewFilter.Null; - if (resetAction != null) - { - foreach (var item in lookup) - { - foreach (var (value, view) in item) - { - resetAction(value, view); - } - } - } - } - } - - public bool Contains(TKey key) - { - lock (SyncRoot) - { - return lookup.Contains(key); - } - } - - public void Dispose() - { - source.CollectionChanged -= SourceCollectionChanged; - } - - public INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged() - { - lock (SyncRoot) - { - return new NotifyCollectionChangedSynchronizedView(this); - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IEnumerator<(T Value, TView View)> IEnumerable<(T Value, TView View)>.GetEnumerator() - { - throw new NotImplementedException(); - } - - public IEnumerator> GetEnumerator() - { - throw new NotImplementedException(); - } - - private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs e) - { - lock (SyncRoot) - { - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - if (e.IsSingleItem) - { - var value = e.NewItem; - var key = keySelector(value); - var view = viewSelector(value); - lookup.Add(key, (value, view)); - filter.InvokeOnAdd(value, view); - } - else - { - foreach (var value in e.NewItems) - { - var key = keySelector(value); - var view = viewSelector(value); - lookup.Add(key, (value, view)); - filter.InvokeOnAdd(value, view); - } - } - break; - case NotifyCollectionChangedAction.Remove: - if (e.IsSingleItem) - { - var value = e.OldItem; - var key = keySelector(value); - - // TODO:... - // lookup - - //var removeItems = lookup[key]; - //foreach (var v in removeItems) - //{ - // filter.InvokeOnRemove(v); - //} - - //lookup.Remove(key); - //filter.InvokeOnRemove( - //lookup - //dict.Remove((value, id), out var v); - //filter.InvokeOnRemove(v.Value, v.View); - } - else - { - //foreach (var value in e.OldItems) - //{ - // var id = identitySelector(value); - // dict.Remove((value, id), out var v); - // filter.InvokeOnRemove(v.Value, v.View); - //} - } - - break; - case NotifyCollectionChangedAction.Replace: - break; - case NotifyCollectionChangedAction.Move: - break; - case NotifyCollectionChangedAction.Reset: - break; - default: - break; - } - } - } - } -} diff --git a/src/ObservableCollections/Internal/SortedView.cs b/src/ObservableCollections/Internal/SortedView.cs index a164ae7..7dec6d8 100644 --- a/src/ObservableCollections/Internal/SortedView.cs +++ b/src/ObservableCollections/Internal/SortedView.cs @@ -145,8 +145,6 @@ namespace ObservableCollections.Internal } break; case NotifyCollectionChangedAction.Replace: - // TODO:Range support - // ReplaceRange is not supported in all ObservableCollections collections // Replace is remove old item and insert new item. { diff --git a/src/ObservableCollections/NotifyCollectionChangedEventArgs.cs b/src/ObservableCollections/NotifyCollectionChangedEventArgs.cs index 6e1138b..8353c81 100644 --- a/src/ObservableCollections/NotifyCollectionChangedEventArgs.cs +++ b/src/ObservableCollections/NotifyCollectionChangedEventArgs.cs @@ -3,14 +3,6 @@ using System.Collections.Specialized; namespace ObservableCollections { - // TODO:??? - //public interface IGroupedSynchronizedView : ISynchronizedView, IGrouping, IDisposable - //{ - // // void AttachFilter(Func filter); - // // void ResetFilter(); - //} - - /// /// Contract: /// IsSingleItem ? (NewItem, OldItem) : (NewItems, OldItems) @@ -36,9 +28,6 @@ namespace ObservableCollections public readonly int NewStartingIndex; public readonly int OldStartingIndex; - // TODO:is this required? - // byte distinguishedKey; - public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, bool isSingleItem, T newItem = default!, T oldItem = default!, ReadOnlySpan newItems = default, ReadOnlySpan oldItems = default, int newStartingIndex = -1, int oldStartingIndex = -1) { Action = action;