public IObservable <IChangeSet <T> > Run() { return(Observable.Create <IChangeSet <T> >(observer => { var disposable = new CompositeDisposable(); var resultList = new ChangeAwareListWithRefCounts <T>(); lock (_locker) { var sourceLists = Enumerable.Range(0, _source.Count) .Select(_ => new ReferenceCountTracker <T>()) .ToList(); foreach (var pair in _source.Zip(sourceLists, (item, list) => new { Item = item, List = list })) { disposable.Add(pair.Item.Synchronize(_locker).Subscribe(changes => { CloneSourceList(pair.List, changes); var notifications = UpdateResultList(changes, sourceLists, resultList); if (notifications.Count != 0) { observer.OnNext(notifications); } })); } } return disposable; })); }
public IObservable <IChangeSet <T> > Run() { return(Observable.Create <IChangeSet <T> >(observer => { var disposable = new CompositeDisposable(); var sourceLists = new List <ReferenceCountTracker <T> >(); var resultList = new ChangeAwareListWithRefCounts <T>(); lock (_locker) { foreach (var item in _source) { var list = new ReferenceCountTracker <T>(); sourceLists.Add(list); disposable.Add(item.Synchronize(_locker).Subscribe(changes => { CloneSourceList(list, changes); var notifications = UpdateResultList(changes, sourceLists, resultList); if (notifications.Count != 0) { observer.OnNext(notifications); } })); } } return disposable; })); }
private void UpdateItemMembership(T item, MergeContainer[] sourceLists, ChangeAwareListWithRefCounts <T> resultList) { var isInResult = resultList.Contains(item); var shouldBeInResult = MatchesConstraint(sourceLists, item); if (shouldBeInResult && !isInResult) { resultList.Add(item); } else if (!shouldBeInResult && isInResult) { resultList.Remove(item); } }
private void ProcessItem(MergeContainer[] sourceLists, ChangeAwareListWithRefCounts <T> resultingList, T item) { //check whether the item should be removed from the list var isInResult = resultingList.Contains(item); var shouldBeInResult = MatchesConstraint(sourceLists, item); if (shouldBeInResult) { if (!isInResult) { resultingList.Add(item); } } else { if (isInResult) { resultingList.Remove(item); } } }
private IChangeSet <T> UpdateResultList(MergeContainer[] sourceLists, ChangeAwareListWithRefCounts <T> resultList, IChangeSet <T> changes) { //child caches have been updated before we reached this point. foreach (var change in changes.Flatten()) { switch (change.Reason) { case ListChangeReason.Add: case ListChangeReason.Remove: UpdateItemMembership(change.Current, sourceLists, resultList); break; case ListChangeReason.Replace: UpdateItemMembership(change.Previous.Value, sourceLists, resultList); UpdateItemMembership(change.Current, sourceLists, resultList); break; // Pass through refresh changes: case ListChangeReason.Refresh: resultList.Refresh(change.Current); break; // A move does not affect contents and so can be ignored: case ListChangeReason.Moved: break; // These should not occur as they are replaced by the Flatten operator: //case ListChangeReason.AddRange: //case ListChangeReason.RemoveRange: //case ListChangeReason.Clear: default: throw new ArgumentOutOfRangeException(nameof(change.Reason), "Unsupported change type"); } } return(resultList.CaptureChanges()); }
private IChangeSet <T> UpdateResultList(IChangeSet <T> changes, List <ReferenceCountTracker <T> > sourceLists, ChangeAwareListWithRefCounts <T> resultList) { //child caches have been updated before we reached this point. foreach (var change in changes.Flatten()) { var item = change.Current; var isInResult = resultList.Contains(item); var shouldBeInResult = MatchesConstraint(sourceLists, item); if (shouldBeInResult) { if (!isInResult) { resultList.Add(item); } else if (change.Reason == ListChangeReason.Refresh) { resultList.Refresh(change.Current); } } else { if (isInResult) { resultList.Remove(item); } } } return(resultList.CaptureChanges()); }
public IObservable <IChangeSet <T> > Run() { return(Observable.Create <IChangeSet <T> >(observer => { //this is the resulting list which produces all notifications var resultList = new ChangeAwareListWithRefCounts <T>(); //Transform to a merge container. //This populates a RefTracker when the original source is subscribed to var sourceLists = _source.Connect() .Synchronize(_locker) .Transform(changeset => new MergeContainer(changeset)) .AsObservableList(); //merge the items back together var allChanges = sourceLists.Connect() .MergeMany(mc => mc.Source) .Synchronize(_locker) .Subscribe(changes => { //Populate result list and chck for changes var notifications = UpdateResultList(sourceLists.Items.AsArray(), resultList, changes); if (notifications.Count != 0) { observer.OnNext(notifications); } }); //when an list is removed, need to var removedItem = sourceLists.Connect() .OnItemRemoved(mc => { //Remove items if required var notifications = ProcessChanges(sourceLists.Items.AsArray(), resultList, mc.Tracker.Items); if (notifications.Count != 0) { observer.OnNext(notifications); } if (_type == CombineOperator.And || _type == CombineOperator.Except) { var itemsToCheck = sourceLists.Items.SelectMany(mc2 => mc2.Tracker.Items).ToArray(); var notification2 = ProcessChanges(sourceLists.Items.AsArray(), resultList, itemsToCheck); if (notification2.Count != 0) { observer.OnNext(notification2); } } }) .Subscribe(); //when an list is added or removed, need to var sourceChanged = sourceLists.Connect() .WhereReasonsAre(ListChangeReason.Add, ListChangeReason.AddRange) .ForEachItemChange(mc => { var notifications = ProcessChanges(sourceLists.Items.AsArray(), resultList, mc.Current.Tracker.Items); if (notifications.Count != 0) { observer.OnNext(notifications); } if (_type == CombineOperator.And || _type == CombineOperator.Except) { var notification2 = ProcessChanges(sourceLists.Items.AsArray(), resultList, resultList.ToArray()); if (notification2.Count != 0) { observer.OnNext(notification2); } } }) .Subscribe(); return new CompositeDisposable(sourceLists, allChanges, removedItem, sourceChanged); })); }
private IChangeSet <T> ProcessChanges(MergeContainer[] sourceLists, ChangeAwareListWithRefCounts <T> resultingList, IEnumerable <T> items) { //check whether the item should be removed from the list items.ForEach(item => { ProcessItem(sourceLists, resultingList, item); }); return(resultingList.CaptureChanges()); }
private IChangeSet <T> UpdateResultList(MergeContainer[] sourceLists, ChangeAwareListWithRefCounts <T> resultingList, IChangeSet <T> changes) { //child caches have been updated before we reached this point. changes.Flatten().ForEach(change => { ProcessItem(sourceLists, resultingList, change.Current); }); return(resultingList.CaptureChanges()); }
private IChangeSet <T> UpdateItemSetMemberships(MergeContainer[] sourceLists, ChangeAwareListWithRefCounts <T> resultingList, IEnumerable <T> items) { items.ForEach(item => UpdateItemMembership(item, sourceLists, resultingList)); return(resultingList.CaptureChanges()); }