public IObservable <IChangeSet <T> > Run() { return(Observable.Create <IChangeSet <T> >(observer => { var locker = new object(); var orginal = new ChangeAwareList <T>(); var target = new ChangeAwareList <T>(); var changed = _source.Synchronize(locker).Select(changes => { if (_resetThreshold > 1) { orginal.Clone(changes); } return changes.TotalChanges > _resetThreshold && _comparer != null ? Reset(orginal, target) : Process(target, changes); }); var resort = _resort.Synchronize(locker).Select(changes => Reorder(target)); var changeComparer = _comparerObservable.Synchronize(locker).Select(comparer => ChangeComparer(target, comparer)); return changed.Merge(resort).Merge(changeComparer) .Where(changes => changes.Count != 0) .SubscribeSafe(observer); })); }
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()); }
public IObservable <IPageChangeSet <T> > Run() { return(Observable.Create <IPageChangeSet <T> >(observer => { var locker = new object(); var all = new List <T>(); var paged = new ChangeAwareList <T>(); IPageRequest parameters = new PageRequest(0, 25); var requestStream = _requests .Synchronize(locker) .Select(request => { parameters = request; return CheckParametersAndPage(all, paged, request); }); var datachanged = _source .Synchronize(locker) .Select(changes => Page(all, paged, parameters, changes)); return requestStream.Merge(datachanged) .Where(changes => changes != null && changes.Count != 0) .SubscribeSafe(observer); })); }
public IObservable <IVirtualChangeSet <T> > Run() { return(Observable.Create <IVirtualChangeSet <T> >(observer => { var locker = new object(); var all = new List <T>(); var virtualised = new ChangeAwareList <T>(); IVirtualRequest parameters = new VirtualRequest(0, 25); var requestStream = _requests .Synchronize(locker) .Select(request => { parameters = request; return CheckParamsAndVirtualise(all, virtualised, request); }); var datachanged = _source .Synchronize(locker) .Select(changes => Virtualise(all, virtualised, parameters, changes)); //TODO: Remove this shared state stuff ie. _parameters return requestStream.Merge(datachanged) .Where(changes => changes != null && changes.Count != 0) .Select(changes => new VirtualChangeSet <T>(changes, new VirtualResponse(virtualised.Count, parameters.StartIndex, all.Count))) .SubscribeSafe(observer); })); }
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()); }
public IObservable <IChangeSet <TDestination> > Run() { return(Observable.Create <IChangeSet <TDestination> >(observer => { var transformed = new ChangeAwareList <TDestination>(); return _source.Select(changes => Process(transformed, changes)).NotEmpty().SubscribeSafe(observer); })); }
private IChangeSet <T> Page(List <T> all, ChangeAwareList <T> paged, IChangeSet <T> changeset = null) { if (changeset != null) { all.Clone(changeset); } var previous = paged; int pages = CalculatePages(all); 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()); }
private PageChangeSet <T> CheckParametersAndPage(List <T> all, ChangeAwareList <T> paged, IPageRequest request) { if (request == null || request.Page < 0 || request.Size < 1) { return(null); } return(Page(all, paged, request)); }
private IChangeSet <T> CheckParamsAndVirtualise(List <T> all, ChangeAwareList <T> virtualised, IVirtualRequest request) { if (request == null || request.StartIndex < 0 || request.Size < 1) { return(null); } return(Virtualise(all, virtualised, request)); }
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()); }
private IChangeSet <T> Virtualise(List <T> all, ChangeAwareList <T> virtualised, IVirtualRequest request) { if (request == null || request.StartIndex < 0 || request.Size < 1) { return(null); } _parameters = request; return(Virtualise(all, virtualised)); }
private int GetInsertPositionLinear(ChangeAwareList <T> target, T item) { for (var i = 0; i < target.Count; i++) { if (_comparer.Compare(item, target[i]) < 0) { return(i); } } return(target.Count); }
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 int GetInsertPositionBinary(ChangeAwareList <T> target, T item) { int index = target.BinarySearch(item, _comparer); int insertIndex = ~index; //sort is not returning uniqueness if (insertIndex < 0) { throw new SortException("Binary search has been specified, yet the sort does not yeild uniqueness"); } return(insertIndex); }
private IChangeSet <T> Process(ChangeAwareList <T> target, 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); target.RemoveMany(removed); return(target.CaptureChanges()); } return(ProcessImpl(target, changes)); }
private IChangeSet <T> Virtualise(List <T> all, ChangeAwareList <T> virtualised, IVirtualRequest request, IChangeSet <T> changeset = null) { if (changeset != null) { all.Clone(changeset); } var previous = virtualised; var current = all.Skip(request.StartIndex) .Take(request.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(request.StartIndex, request.StartIndex + request.Size)); foreach (var change in moves) { //check whether an item has moved within the same page var currentIndex = change.Item.CurrentIndex - request.StartIndex; var previousIndex = change.Item.PreviousIndex - request.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 <TValue> > Run() { return(Observable.Create <IChangeSet <TValue> >(observer => { var valueCounters = new Dictionary <TValue, int>(); var result = new ChangeAwareList <TValue>(); return _source.Transform(t => new ItemWithValue <T, TValue>(t, _valueSelector(t))) .Select(changes => Process(valueCounters, result, changes)) .NotEmpty() .SubscribeSafe(observer); })); }
private int GetCurrentPosition(ChangeAwareList <T> target, T item) { int index = _sortOptions == SortOptions.UseBinarySearch ? target.BinarySearch(item, _comparer) : target.IndexOf(item); if (index < 0) { throw new SortException("Current item cannot be found"); } return(index); }
public IObservable <IChangeSet <IGroup <TObject, TGroupKey> > > Run() { return(Observable.Create <IChangeSet <IGroup <TObject, TGroupKey> > >(observer => { var groupings = new ChangeAwareList <IGroup <TObject, TGroupKey> >(); var groupCache = new Dictionary <TGroupKey, Group <TObject, TGroupKey> >(); return _source.Transform(t => new ItemWithValue <TObject, TGroupKey>(t, _groupSelector(t))) .Select(changes => Process(groupings, groupCache, changes)) .DisposeMany() //dispose removes as the grouping is disposable .NotEmpty() .SubscribeSafe(observer); })); }
private IChangeSet <T> Page(List <T> all, ChangeAwareList <T> paged, IPageRequest request) { if (request == null || request.Page < 0 || request.Size < 1) { return(null); } if (request.Size == _parameters.Size && request.Page == _parameters.Page) { return(null); } _parameters = request; return(Page(all, paged)); }
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()); }
public IObservable <IChangeSet <T> > Run() { return(Observable.Create <IChangeSet <T> >(observer => { var locker = new object(); var all = new List <T>(); var virtualised = new ChangeAwareList <T>(); var requestStream = _requests .Synchronize(locker) .Select(request => Virtualise(all, virtualised, request)); var datachanged = _source .Synchronize(locker) .Select(changes => Virtualise(all, virtualised, changes)); return requestStream.Merge(datachanged) .Where(changes => changes != null && changes.Count != 0) .SubscribeSafe(observer); })); }
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()); })); }
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()); }
public IObservable <IChangeSet <IGroup <TObject, TGroupKey> > > Run() { return(Observable.Create <IChangeSet <IGroup <TObject, TGroupKey> > >(observer => { var groupings = new ChangeAwareList <IGroup <TObject, TGroupKey> >(); var groupCache = new Dictionary <TGroupKey, Group <TObject, TGroupKey> >(); var itemsWithGroup = _source .Transform(t => new ItemWithValue <TObject, TGroupKey>(t, _groupSelector(t))) .AsObservableList(); var locker = new object(); var shared = itemsWithGroup.Connect().Synchronize(locker).Publish(); var grouper = shared .Select(changes => Process(groupings, groupCache, changes)); IObservable <IChangeSet <IGroup <TObject, TGroupKey> > > regrouper; if (_regrouper == null) { regrouper = Observable.Never <IChangeSet <IGroup <TObject, TGroupKey> > >(); } else { regrouper = _regrouper.Synchronize(locker) .CombineLatest(shared.ToCollection(), (_, collection) => Regroup(groupings, groupCache, collection)); } var publisher = grouper.Merge(regrouper) .DisposeMany() //dispose removes as the grouping is disposable .NotEmpty() .SubscribeSafe(observer); return new CompositeDisposable(itemsWithGroup, publisher, shared.Connect()); })); }
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()); }
//TODO: Need to account for re-evaluate (as it is not mutually excluse to clear and replace) private void Requery(Func <T, bool> predicate, List <ItemWithMatch> allWithMatch, List <T> all, ChangeAwareList <T> filtered) { _predicate = predicate; var newState = allWithMatch.Select(item => { var match = _predicate(item.Item); var wasMatch = item.IsMatch; //reflect filtered state if (item.IsMatch != match) { item.IsMatch = match; } return(new { Item = item, IsMatch = match, WasMatch = wasMatch }); }).ToList(); //reflect items which are no longer matched var noLongerMatched = newState.Where(state => !state.IsMatch && state.WasMatch).Select(state => state.Item.Item); filtered.RemoveMany(noLongerMatched); //reflect new matches in the list var newMatched = newState.Where(state => state.IsMatch && !state.WasMatch).Select(state => state.Item.Item); filtered.AddRange(newMatched); }
private IChangeSet <TValue> Process(Dictionary <TValue, int> valueCounters, ChangeAwareList <TValue> result, IChangeSet <ItemWithValue <T, TValue> > changes) { 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 changes) { 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)) { continue; } 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()); }