private IChangeSet <T> ProcessImpl(IChangeSet <T> changes) { changes.ForEach(change => { switch (change.Reason) { case ListChangeReason.Add: { var current = change.Item.Current; Insert(current); break; } case ListChangeReason.AddRange: { var ordered = change.Range.OrderBy(t => t, _comparer).ToList(); if (_innerList.Count == 0) { _innerList.AddRange(ordered); } else { ordered.ForEach(Insert); } break; } case ListChangeReason.Replace: { var current = change.Item.Current; //TODO: check whether an item should stay in the same position //i.e. update and move Remove(change.Item.Previous.Value); Insert(current); break; } case ListChangeReason.Remove: { var current = change.Item.Current; Remove(current); break; } case ListChangeReason.RemoveRange: { _innerList.RemoveMany(change.Range); break; } case ListChangeReason.Clear: { _innerList.Clear(); break; } } }); return(_innerList.CaptureChanges()); }
private void RegisterForRemoval(IChangeSet <T> changes) { changes.ForEach(change => { switch (change.Reason) { case ListChangeReason.Replace: change.Item.Previous.IfHasValue(t => _callback(t)); break; case ListChangeReason.Remove: _callback(change.Item.Current); break; case ListChangeReason.RemoveRange: change.Range.ForEach(_callback); break; case ListChangeReason.Clear: _items.ForEach(_callback); break; } }); _items.Clone(changes); }
public void RegisterForRemoval(IChangeSet <TObject, TKey> changes, Cache <TObject, TKey> cache) { changes.ForEach(change => { switch (change.Reason) { case ChangeReason.Update: change.Previous.IfHasValue(t => _removeAction(t)); break; case ChangeReason.Remove: _removeAction(change.Current); break; } }); cache.Clone(changes); }
private void RegisterForRemoval(IChangeSet <TObject, TKey> changes, InternalCache <TObject, TKey> cache) { changes.ForEach(change => { switch (change.Reason) { case ChangeReason.Update: // ReSharper disable once InconsistentlySynchronizedField change.Previous.IfHasValue(t => _removeAction(t)); break; case ChangeReason.Remove: // ReSharper disable once InconsistentlySynchronizedField _removeAction(change.Current); break; } }); cache.Clone(changes); }
private void CloneSourceList(ReferenceCountTracker <T> tracker, IChangeSet <T> changes) { changes.ForEach(change => { switch (change.Reason) { case ListChangeReason.Add: tracker.Add(change.Item.Current); break; case ListChangeReason.AddRange: foreach (var t in change.Range) { tracker.Add(t); } break; case ListChangeReason.Replace: tracker.Remove(change.Item.Previous.Value); tracker.Add(change.Item.Current); break; case ListChangeReason.Remove: tracker.Remove(change.Item.Current); break; case ListChangeReason.RemoveRange: case ListChangeReason.Clear: foreach (var t in change.Range) { tracker.Remove(t); } break; //case ListChangeReason.Clear: // tracker.Clear(); // break; } }); }
private void DoUpdate(IChangeSet <TObject, TKey> updates, IObservableCollection <TObject> list) { updates.ForEach(update => { switch (update.Reason) { case ChangeReason.Add: list.Add(update.Current); break; case ChangeReason.Remove: list.Remove(update.Current); break; case ChangeReason.Update: { list.Remove(update.Previous.Value); list.Add(update.Current); } break; } }); }
public IDictionary <object, IChange> Visit(IChangeSet changeSet) { var distinct = new Dictionary <object, IChange>(); changeSet.ForEach(change => { /* * recuperiamo un riferimento alle entities * che sono oggetto della modifica */ change.GetChangedEntities().ForEach(entity => { if (!distinct.ContainsKey(entity)) { /* * se l'entity non è tra quelle che abbiamo * già incontrato la aggiungiamo. */ distinct.Add(entity, change); } else { /* * Se l'entity è già tra quelle visitate * sostituiamo la IChange associata perchè * la IChange più importante è l'ultima, che è * quella che determina la ProposedActions che * verrà proposta. */ distinct[entity] = change; } }); }); return(distinct); }
public IDictionary<object, IChange> Visit( IChangeSet changeSet ) { var distinct = new Dictionary<object, IChange>(); changeSet.ForEach( change => { /* * recuperiamo un riferimento alle entities * che sono oggetto della modifica */ change.GetChangedEntities().ForEach( entity => { if( !distinct.ContainsKey( entity ) ) { /* * se l'entity non è tra quelle che abbiamo * già incontrato la aggiungiamo. */ distinct.Add( entity, change ); } else { /* * Se l'entity è già tra quelle visitate * sostituiamo la IChange associata perchè * la IChange più importante è l'ultima, che è * quella che determina la ProposedActions che * verrà proposta. */ distinct[ entity ] = change; } } ); } ); return distinct; }
private IChangeSet <TValue> Process(IChangeSet <ItemWithValue <T, TValue> > updates) { Action <TValue> addAction = value => _valueCounters.Lookup(value) .IfHasValue(count => _valueCounters[value] = count + 1) .Else(() => { _valueCounters[value] = 1; _result.Add(value); }); Action <TValue> removeAction = value => { var counter = _valueCounters.Lookup(value); if (!counter.HasValue) { return; } //decrement counter var newCount = counter.Value - 1; _valueCounters[value] = newCount; if (newCount != 0) { return; } //if there are none, then remove and notify _result.Remove(value); }; updates.ForEach(change => { switch (change.Reason) { case ListChangeReason.Add: { var value = change.Item.Current.Value; addAction(value); break; } case ListChangeReason.AddRange: { change.Range.Select(item => item.Value).ForEach(addAction); break; } // case ListChangeReason.Evaluate: case ListChangeReason.Replace: { var value = change.Item.Current.Value; var previous = change.Item.Previous.Value.Value; if (value.Equals(previous)) { return; } removeAction(previous); addAction(value); break; } case ListChangeReason.Remove: { var previous = change.Item.Current.Value; removeAction(previous); break; } case ListChangeReason.RemoveRange: { change.Range.Select(item => item.Value).ForEach(removeAction); break; } case ListChangeReason.Clear: { _result.Clear(); _valueCounters.Clear(); break; } } }); return(_result.CaptureChanges()); }
private void Transform(IChangeSet <TSource> changes) { if (changes == null) { throw new ArgumentNullException(nameof(changes)); } _transformed.EnsureCapacityFor(changes); changes.ForEach(item => { switch (item.Reason) { case ListChangeReason.Add: { var change = item.Item; if (change.CurrentIndex < 0 | change.CurrentIndex >= _transformed.Count) { _transformed.Add(_containerFactory(change.Current)); } else { _transformed.Insert(change.CurrentIndex, _containerFactory(change.Current)); } break; } case ListChangeReason.AddRange: { _transformed.AddOrInsertRange(item.Range.Select(_containerFactory), item.Range.Index); break; } case ListChangeReason.Replace: { var change = item.Item; if (change.CurrentIndex == change.PreviousIndex) { _transformed[change.CurrentIndex] = _containerFactory(change.Current); } else { _transformed.RemoveAt(change.PreviousIndex); _transformed.Insert(change.CurrentIndex, _containerFactory(change.Current)); } } break; case ListChangeReason.Remove: { var change = item.Item; bool hasIndex = change.CurrentIndex >= 0; if (hasIndex) { _transformed.RemoveAt(item.Item.CurrentIndex); } else { var toremove = _transformed.FirstOrDefault(t => ReferenceEquals(t.Source, t)); if (toremove != null) { _transformed.Remove(toremove); } } } break; case ListChangeReason.RemoveRange: { if (item.Range.Index >= 0) { _transformed.RemoveRange(item.Range.Index, item.Range.Count); } else { var toremove = _transformed.Where(t => ReferenceEquals(t.Source, t)).ToArray(); _transformed.RemoveMany(toremove); } } break; case ListChangeReason.Clear: { //i.e. need to store transformed reference so we can correctly clear var toClear = new Change <TransformedItemContainer>(ListChangeReason.Clear, _transformed); _transformed.ClearOrRemoveMany(toClear); } break; case ListChangeReason.Moved: { var change = item.Item; bool hasIndex = change.CurrentIndex >= 0; if (!hasIndex) { throw new UnspecifiedIndexException("Cannot move as an index was not specified"); } var collection = _transformed as IExtendedList <TransformedItemContainer>; if (collection != null) { collection.Move(change.PreviousIndex, change.CurrentIndex); } else { var current = _transformed[change.PreviousIndex]; _transformed.RemoveAt(change.PreviousIndex); _transformed.Insert(change.CurrentIndex, current); } break; } } }); }
/// <summary> /// Clones the source list with the specified change set, transforming the items using the specified factory /// </summary> /// <typeparam name="TSource">The type of the source.</typeparam> /// <typeparam name="TDestination">The type of the destination.</typeparam> /// <param name="source">The source.</param> /// <param name="changes">The changes.</param> /// <param name="transformFactory">The transform factory.</param> /// <exception cref="System.ArgumentNullException"> /// source /// or /// changes /// or /// transformFactory /// </exception> public static void Transform <TSource, TDestination>(this IList <TDestination> source, IChangeSet <TSource> changes, Func <TSource, TDestination> transformFactory) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (changes == null) { throw new ArgumentNullException(nameof(changes)); } if (transformFactory == null) { throw new ArgumentNullException(nameof(transformFactory)); } source.EnsureCapacityFor(changes); changes.ForEach(item => { switch (item.Reason) { case ListChangeReason.Add: { var change = item.Item; source.Insert(change.CurrentIndex, transformFactory(change.Current)); break; } case ListChangeReason.AddRange: { source.AddOrInsertRange(item.Range.Select(transformFactory), item.Range.Index); break; } case ListChangeReason.Replace: { var change = item.Item; if (change.CurrentIndex == change.PreviousIndex) { source[change.CurrentIndex] = transformFactory(change.Current); } else { source.RemoveAt(change.PreviousIndex); source.Insert(change.CurrentIndex, transformFactory(change.Current)); } } break; case ListChangeReason.Remove: { source.RemoveAt(item.Item.CurrentIndex); } break; case ListChangeReason.RemoveRange: { source.RemoveRange(item.Range.Index, item.Range.Count); } break; case ListChangeReason.Clear: { source.Clear(); } break; } }); }
internal static void CloneReactiveList <T>(this ReactiveList <T> source, IChangeSet <T> changes) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (changes == null) { throw new ArgumentNullException(nameof(changes)); } changes.ForEach(item => { switch (item.Reason) { case ListChangeReason.Add: { var change = item.Item; var hasIndex = change.CurrentIndex >= 0; if (hasIndex) { source.Insert(change.CurrentIndex, change.Current); } else { source.Add(change.Current); } break; } case ListChangeReason.AddRange: { var startingIndex = item.Range.Index; if (RxApp.SupportsRangeNotifications) { if (startingIndex >= 0) { source.InsertRange(startingIndex, item.Range); } else { source.AddRange(item.Range); } } else { if (startingIndex >= 0) { item.Range.Reverse().ForEach(t => source.Insert(startingIndex, t)); } else { item.Range.ForEach(source.Add); } } break; } case ListChangeReason.Clear: { source.Clear(); break; } case ListChangeReason.Replace: { var change = item.Item; bool hasIndex = change.CurrentIndex >= 0; if (hasIndex && change.CurrentIndex == change.PreviousIndex) { source[change.CurrentIndex] = change.Current; } else { source.RemoveAt(change.PreviousIndex); source.Insert(change.CurrentIndex, change.Current); } } break; case ListChangeReason.Remove: { var change = item.Item; bool hasIndex = change.CurrentIndex >= 0; if (hasIndex) { source.RemoveAt(change.CurrentIndex); } else { source.Remove(change.Current); } break; } case ListChangeReason.RemoveRange: { if (RxApp.SupportsRangeNotifications && item.Range.Index >= 0) { source.RemoveRange(item.Range.Index, item.Range.Count); } else { source.RemoveMany(item.Range); } } break; case ListChangeReason.Moved: { var change = item.Item; bool hasIndex = change.CurrentIndex >= 0; if (!hasIndex) { throw new UnspecifiedIndexException("Cannot move as an index was not specified"); } source.Move(change.PreviousIndex, change.CurrentIndex); break; } } }); }
private void UpdateResultList(MergeContainer[] sourceLists, IChangeSet <TObject, TKey> changes) { changes.ForEach(change => { ProcessItem(sourceLists, change.Current, change.Key); }); }
/// <summary> /// Clones the list from the specified change set /// </summary> /// <typeparam name="T"></typeparam> /// <param name="source">The source.</param> /// <param name="changes">The changes.</param> /// <exception cref="System.ArgumentNullException"> /// source /// or /// changes /// </exception> public static void Clone <T>(this IList <T> source, IChangeSet <T> changes) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (changes == null) { throw new ArgumentNullException(nameof(changes)); } changes.ForEach(item => { switch (item.Reason) { case ListChangeReason.Add: { var change = item.Item; bool hasIndex = change.CurrentIndex >= 0; if (hasIndex) { source.Insert(change.CurrentIndex, change.Current); } else { source.Add(change.Current); } break; } case ListChangeReason.AddRange: { source.AddOrInsertRange(item.Range, item.Range.Index); break; } case ListChangeReason.Clear: { source.Clear(); break; } case ListChangeReason.Replace: { var change = item.Item; bool hasIndex = change.CurrentIndex >= 0; if (hasIndex && change.CurrentIndex == change.PreviousIndex) { source[change.CurrentIndex] = change.Current; } else { //is this best? or replace + move? source.RemoveAt(change.PreviousIndex); source.Insert(change.CurrentIndex, change.Current); } } break; case ListChangeReason.Remove: { var change = item.Item; bool hasIndex = change.CurrentIndex >= 0; if (hasIndex) { source.RemoveAt(change.CurrentIndex); } else { source.Remove(change.Current); } break; } case ListChangeReason.RemoveRange: { //ignore this case because WhereReasonsAre removes the index [in which case call RemoveMany] //if (item.Range.Index < 0) // throw new UnspecifiedIndexException("ListChangeReason.RemoveRange should not have an index specified index"); if (item.Range.Index >= 0 && (source is IExtendedList <T> || source is List <T>)) { source.RemoveRange(item.Range.Index, item.Range.Count); } else { source.RemoveMany(item.Range); } } break; case ListChangeReason.Moved: { var change = item.Item; bool hasIndex = change.CurrentIndex >= 0; if (!hasIndex) { throw new UnspecifiedIndexException("Cannot move as an index was not specified"); } var collection = source as IExtendedList <T>; if (collection != null) { collection.Move(change.PreviousIndex, change.CurrentIndex); } else { //check this works whatever the index is source.RemoveAt(change.PreviousIndex); source.Insert(change.CurrentIndex, change.Current); } break; } } }); }
public IDistinctChangeSet <TValue> Calculate(IChangeSet <TObject, TKey> updates) { var result = new List <Change <TValue, TValue> >(); Action <TValue> addAction = value => _valueCounters.Lookup(value) .IfHasValue(count => _valueCounters[value] = count + 1) .Else(() => { _valueCounters[value] = 1; result.Add(new Change <TValue, TValue>(ChangeReason.Add, value, value)); }); Action <TValue> removeAction = value => { var counter = _valueCounters.Lookup(value); if (!counter.HasValue) { return; } //decrement counter var newCount = counter.Value - 1; _valueCounters[value] = newCount; if (newCount != 0) { return; } //if there are none, then remove and notify _valueCounters.Remove(value); result.Add(new Change <TValue, TValue>(ChangeReason.Remove, value, value)); }; updates.ForEach(change => { var key = change.Key; switch (change.Reason) { case ChangeReason.Add: { var value = _valueSelector(change.Current); addAction(value); _itemCache[key] = value; break; } case ChangeReason.Evaluate: case ChangeReason.Update: { var value = _valueSelector(change.Current); var previous = _itemCache[key]; if (value.Equals(previous)) { return; } removeAction(previous); addAction(value); _itemCache[key] = value; break; } case ChangeReason.Remove: { var previous = _itemCache[key]; removeAction(previous); _itemCache.Remove(key); break; } } }); return(new DistinctChangeSet <TValue>(result)); }
/// <summary> /// Filters the source from the changes, using the specified predicate /// </summary> /// <typeparam name="T"></typeparam> /// <param name="source">The source.</param> /// <param name="changes">The changes.</param> /// <param name="predicate">The predicate.</param> public static void Filter <T>(this IList <T> source, IChangeSet <T> changes, Func <T, bool> predicate) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (changes == null) { throw new ArgumentNullException(nameof(changes)); } if (predicate == null) { throw new ArgumentNullException(nameof(predicate)); } //TODO: Check for missing index changes.ForEach(item => { switch (item.Reason) { case ListChangeReason.Add: { var change = item.Item; var match = predicate(change.Current); if (match) { source.Add(change.Current); } } break; case ListChangeReason.AddRange: { var matches = item.Range.Where(predicate).ToList(); source.AddRange(matches); } break; case ListChangeReason.Replace: { var change = item.Item; var match = predicate(change.Current); var wasMatch = predicate(change.Previous.Value); if (match) { if (wasMatch) { //an update, so get the latest index var previous = source.FindItemAndIndex(change.Previous.Value, ReferenceEqualityComparer <T> .Instance) .ValueOrThrow(() => new InvalidOperationException("Cannot find item. Expected to be in the list")); //replace inline source[previous.Index] = change.Current; } else { source.Add(change.Current); } } else { if (wasMatch) { source.Remove(change.Previous.Value); } } } break; case ListChangeReason.Remove: { var change = item.Item; var wasMatch = predicate(change.Current); if (wasMatch) { source.Remove(change.Current); } } break; case ListChangeReason.RemoveRange: { source.RemoveMany(item.Range.Where(predicate)); } break; case ListChangeReason.Clear: { source.Clear(); } break; } }); }
/// <summary> /// Clones the source list with the specified change set, transforming the items using the specified factory /// </summary> /// <typeparam name="TSource">The type of the source.</typeparam> /// <typeparam name="TDestination">The type of the destination.</typeparam> /// <param name="source">The source.</param> /// <param name="changes">The changes.</param> /// <param name="transformFactory">The transform factory.</param> /// <exception cref="System.ArgumentNullException"> /// source /// or /// changes /// or /// transformFactory /// </exception> public static void Transform <TSource, TDestination>(this IList <TDestination> source, IChangeSet <TSource> changes, Func <TSource, TDestination> transformFactory) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (changes == null) { throw new ArgumentNullException(nameof(changes)); } if (transformFactory == null) { throw new ArgumentNullException(nameof(transformFactory)); } source.EnsureCapacityFor(changes); changes.ForEach(item => { switch (item.Reason) { case ListChangeReason.Add: { var change = item.Item; source.Insert(change.CurrentIndex, transformFactory(change.Current)); break; } case ListChangeReason.AddRange: { source.AddOrInsertRange(item.Range.Select(transformFactory), item.Range.Index); break; } case ListChangeReason.Replace: { var change = item.Item; if (change.CurrentIndex == change.PreviousIndex) { source[change.CurrentIndex] = transformFactory(change.Current); } else { source.RemoveAt(change.PreviousIndex); source.Insert(change.CurrentIndex, transformFactory(change.Current)); } } break; case ListChangeReason.Remove: { source.RemoveAt(item.Item.CurrentIndex); } break; case ListChangeReason.RemoveRange: { source.RemoveRange(item.Range.Index, item.Range.Count); } break; case ListChangeReason.Clear: { //TODO: Need to resolve the issue of not being able to use ClearOrRemoveMany()!!! //i.e. need to store transformed reference so we can correctly clear source.Clear(); } break; } }); }