private IChangeSet <T> Process(IChangeSet <T> changes) { //if all removes and not Clear, then more efficient to try clear range if (changes.TotalChanges == changes.Removes && changes.All(c => c.Reason != ListChangeReason.Clear) && changes.Removes > 1) { var removed = changes.Unified().Select(u => u.Current); _innerList.RemoveMany(removed); return(_innerList.CaptureChanges()); } return(ProcessImpl(changes)); }
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 <TDestination> Process(IChangeSet <TSource> changes) { Transform(changes); var changed = _transformed.CaptureChanges(); return(changed.Transform(container => container.Destination)); }
private IChangeSet <T> ProcessImpl(ChangeAwareList <T> target, IChangeSet <T> changes) { foreach (var change in changes) { switch (change.Reason) { case ListChangeReason.Add: { var current = change.Item.Current; Insert(target, current); break; } case ListChangeReason.AddRange: { var ordered = change.Range.OrderBy(t => t, _comparer).ToList(); if (target.Count == 0) { target.AddRange(ordered); } else { ordered.ForEach(item => Insert(target, item)); } 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(target, change.Item.Previous.Value); Insert(target, current); break; } case ListChangeReason.Remove: { var current = change.Item.Current; Remove(target, current); break; } case ListChangeReason.RemoveRange: { target.RemoveMany(change.Range); break; } case ListChangeReason.Clear: { target.Clear(); break; } } } return(target.CaptureChanges()); }
private IChangeSet <T> Reset(ChangeAwareList <T> original, ChangeAwareList <T> target) { var sorted = original.OrderBy(t => t, _comparer).ToList(); target.Clear(); target.AddRange(sorted); return(target.CaptureChanges()); }
private IChangeSet <T> Page(IChangeSet <T> changeset = null) { if (changeset != null) { _all.Clone(changeset); } var previous = _paged; int pages = CalculatePages(); int page = _parameters.Page > pages ? pages : _parameters.Page; int skip = _parameters.Size * (page - 1); var current = _all.Skip(skip) .Take(_parameters.Size) .ToList(); var adds = current.Except(previous); var removes = previous.Except(current); _paged.RemoveMany(removes); adds.ForEach(t => { var index = current.IndexOf(t); _paged.Insert(index, t); }); var startIndex = skip; var moves = changeset.EmptyIfNull() .Where(change => change.Reason == ListChangeReason.Moved && change.MovedWithinRange(startIndex, startIndex + _parameters.Size)); foreach (var change in moves) { //check whether an item has moved within the same page var currentIndex = change.Item.CurrentIndex - startIndex; var previousIndex = change.Item.PreviousIndex - startIndex; _paged.Move(previousIndex, currentIndex); } //find replaces [Is this ever the case that it can be reached] for (int i = 0; i < current.Count; i++) { var currentItem = current[i]; var previousItem = previous[i]; if (ReferenceEquals(currentItem, previousItem)) { continue; } var index = _paged.IndexOf(currentItem); _paged.Move(i, index); } return(_paged.CaptureChanges()); }
public IObservable <IChangeSet <T> > Run() { return(_source.Select(changes => { _filtered.Filter(changes, _predicate); return _filtered.CaptureChanges(); }) .NotEmpty()); }
private IChangeSet <T> ChangeComparer(ChangeAwareList <T> target, IComparer <T> comparer) { _comparer = comparer; var sorted = target.OrderBy(t => t, _comparer).ToList(); target.Clear(); target.AddRange(sorted); return(target.CaptureChanges()); }
public IObservable <IChangeSet <T> > Run() { return(Observable.Create <IChangeSet <T> >(observer => { var filtered = new ChangeAwareList <T>(); return _source.Select(changes => { filtered.Filter(changes, _predicate); return filtered.CaptureChanges(); }).NotEmpty().SubscribeSafe(observer); })); }
private IChangeSet <T> Virtualise(List <T> all, ChangeAwareList <T> virtualised, IChangeSet <T> changeset = null) { if (changeset != null) { all.Clone(changeset); } var previous = virtualised; var current = all.Skip(_parameters.StartIndex) .Take(_parameters.Size) .ToList(); var adds = current.Except(previous); var removes = previous.Except(current); virtualised.RemoveMany(removes); adds.ForEach(t => { var index = current.IndexOf(t); virtualised.Insert(index, t); }); var moves = changeset.EmptyIfNull() .Where(change => change.Reason == ListChangeReason.Moved && change.MovedWithinRange(_parameters.StartIndex, _parameters.StartIndex + _parameters.Size)); foreach (var change in moves) { //check whether an item has moved within the same page var currentIndex = change.Item.CurrentIndex - _parameters.StartIndex; var previousIndex = change.Item.PreviousIndex - _parameters.StartIndex; virtualised.Move(previousIndex, currentIndex); } //find replaces [Is this ever the case that it can be reached] for (var i = 0; i < current.Count; i++) { var currentItem = current[i]; var previousItem = previous[i]; if (ReferenceEquals(currentItem, previousItem)) { continue; } var index = virtualised.IndexOf(currentItem); virtualised.Move(i, index); } return(virtualised.CaptureChanges()); }
public IObservable <IChangeSet <T> > Run() { return(Observable.Create <IChangeSet <T> >(observer => { var allWithMatch = new List <ItemWithMatch>(); var all = new List <T>(); var filtered = new ChangeAwareList <T>(); var locker = new object(); //requery wehn controller either fires changed or requery event var refresher = _predicates.Synchronize(locker) .Select(predicate => { Requery(predicate, allWithMatch, all, filtered); var changed = filtered.CaptureChanges(); return changed; }); var shared = _source.Synchronize(locker).Publish(); //take current filter state of all items var updateall = shared.Synchronize(locker) .Transform(t => new ItemWithMatch(t, _predicate(t))) .Subscribe(allWithMatch.Clone); //filter result list var filter = shared.Synchronize(locker) .Select(changes => { filtered.Filter(changes, _predicate); var changed = filtered.CaptureChanges(); return changed; }); var subscriber = refresher.Merge(filter).NotEmpty().SubscribeSafe(observer); return new CompositeDisposable(updateall, subscriber, shared.Connect()); })); }
public Continuation <IChangeSet <T> > Write(IChangeSet <T> changes) { if (changes == null) { throw new ArgumentNullException(nameof(changes)); } IChangeSet <T> result; lock (_locker) { try { _data.Clone(changes); result = _data.CaptureChanges(); } catch (Exception ex) { return(new Continuation <IChangeSet <T> >(ex)); } } return(new Continuation <IChangeSet <T> >(result)); }
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 IChangeSet <T> Reorder(ChangeAwareList <T> target) { int index = -1; var sorted = target.OrderBy(t => t, _comparer).ToList(); foreach (var item in sorted) { index++; var existing = target[index]; //if item is in the same place, if (ReferenceEquals(item, existing)) { continue; } //Cannot use binary search as Resort is implicit of a mutable change var old = target.IndexOf(item); target.Move(old, index); } return(target.CaptureChanges()); }
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()); }
private IChangeSet <TDestination> Process(IChangeSet <TSource> changes) { _transformed.Transform(changes, _factory); return(_transformed.CaptureChanges()); }