private IChangeSet <TDestination> Process(ChangeAwareList <TDestination> transformed, IChangeSet <TSource> source) { //TODO: This is very inefficient as it flattens range operation //need to find a means of re-forming ranges var children = source.Unified().SelectMany(change => { var many = _manyselector(change.Current); return(many.Select(m => new TransformedItem <TDestination>(change.Reason, m))); }); foreach (var child in children) { switch (child.Reason) { case ListChangeReason.Add: transformed.Add(child.Current); break; case ListChangeReason.Replace: transformed.Remove(child.Previous.Value); transformed.Add(child.Current); break; case ListChangeReason.Remove: transformed.Remove(child.Current); break; case ListChangeReason.Clear: transformed.Clear(); break; } } return(transformed.CaptureChanges()); }
private IChangeSet <IGroup <TObject, TGroupKey> > Regroup(ChangeAwareList <IGroup <TObject, TGroupKey> > result, IDictionary <TGroupKey, Group <TObject, TGroupKey> > groupCollection, IReadOnlyCollection <ItemWithValue <TObject, TGroupKey> > currentItems) { //TODO: We need to update ItemWithValue> foreach (var itemWithValue in currentItems) { var currentGroupKey = itemWithValue.Value; var newGroupKey = _groupSelector(itemWithValue.Item); if (newGroupKey.Equals(currentGroupKey)) { continue; } //remove from the old group var currentGroupLookup = GetCache(groupCollection, currentGroupKey); var currentGroupCache = currentGroupLookup.Group; currentGroupCache.Edit(innerList => innerList.Remove(itemWithValue.Item)); if (currentGroupCache.List.Count == 0) { groupCollection.Remove(currentGroupKey); result.Remove(currentGroupCache); } //Mark the old item with the new cache group itemWithValue.Value = newGroupKey; //add to the new group var newGroupLookup = GetCache(groupCollection, newGroupKey); var newGroupCache = newGroupLookup.Group; newGroupCache.Edit(innerList => innerList.Add(itemWithValue.Item)); if (newGroupLookup.WasCreated) { result.Add(newGroupCache); } } return(result.CaptureChanges()); }
private void Transform(ChangeAwareList <TransformedItemContainer> transformed, IChangeSet <TSource> changes) { if (changes == null) { throw new ArgumentNullException(nameof(changes)); } transformed.EnsureCapacityFor(changes); foreach (var item in changes) { 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; } } } }
private IChangeSet <IGroup <TObject, TGroupKey> > Process(ChangeAwareList <IGroup <TObject, TGroupKey> > result, IDictionary <TGroupKey, Group <TObject, TGroupKey> > groupCollection, IChangeSet <ItemWithValue <TObject, TGroupKey> > changes) { //TODO.This flattened enumerator is inefficient as range operations are lost. //maybe can infer within each grouping whether we can regroup i.e. Another enumerator!!! foreach (var grouping in changes.Unified().GroupBy(change => change.Current.Value)) { //lookup group and if created, add to result set var currentGroup = grouping.Key; var lookup = GetCache(groupCollection, currentGroup); var groupCache = lookup.Group; if (lookup.WasCreated) { result.Add(groupCache); } //start a group edit session, so all changes are batched groupCache.Edit( list => { //iterate through the group's items and process foreach (var change in grouping) { switch (change.Reason) { case ListChangeReason.Add: { list.Add(change.Current.Item); break; } case ListChangeReason.Replace: { var previousItem = change.Previous.Value.Item; var previousGroup = change.Previous.Value.Value; //check whether an item changing has resulted in a different group if (previousGroup.Equals(currentGroup)) { //find and replace var index = list.IndexOf(previousItem); list[index] = change.Current.Item; } else { //add to new group list.Add(change.Current.Item); //remove from old group groupCollection.Lookup(previousGroup) .IfHasValue(g => { g.Edit(oldList => oldList.Remove(previousItem)); if (g.List.Count != 0) { return; } groupCollection.Remove(g.GroupKey); result.Remove(g); }); } break; } case ListChangeReason.Remove: { list.Remove(change.Current.Item); break; } case ListChangeReason.Clear: { list.Clear(); break; } } } }); if (groupCache.List.Count == 0) { groupCollection.Remove(groupCache.GroupKey); result.Remove(groupCache); } } return(result.CaptureChanges()); }
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); }; foreach (var change in updates) { 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(_result.CaptureChanges()); } 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()); }