Example #1
0
        private async Task Transform(ChangeAwareList <TransformedItemContainer> transformed, IChangeSet <TSource> changes)
        {
            if (changes is null)
            {
                throw new ArgumentNullException(nameof(changes));
            }

            foreach (var item in changes)
            {
                switch (item.Reason)
                {
                case ListChangeReason.Add:
                {
                    var change = item.Item;
                    if (change.CurrentIndex < 0 | change.CurrentIndex >= transformed.Count)
                    {
                        var container = await _containerFactory(item.Item.Current).ConfigureAwait(false);

                        transformed.Add(container);
                    }
                    else
                    {
                        var container = await _containerFactory(item.Item.Current).ConfigureAwait(false);

                        transformed.Insert(change.CurrentIndex, container);
                    }

                    break;
                }

                case ListChangeReason.AddRange:
                {
                    var tasks      = item.Range.Select(_containerFactory);
                    var containers = await Task.WhenAll(tasks).ConfigureAwait(false);

                    transformed.AddOrInsertRange(containers, item.Range.Index);
                    break;
                }

                case ListChangeReason.Replace:
                {
                    var change    = item.Item;
                    var container = await _containerFactory(item.Item.Current).ConfigureAwait(false);

                    if (change.CurrentIndex == change.PreviousIndex)
                    {
                        transformed[change.CurrentIndex] = container;
                    }
                    else
                    {
                        transformed.RemoveAt(change.PreviousIndex);
                        transformed.Insert(change.CurrentIndex, container);
                    }

                    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 is not 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");
                    }

                    if (transformed is IExtendedList <TransformedItemContainer> collection)
                    {
                        collection.Move(change.PreviousIndex, change.CurrentIndex);
                    }
                    else
                    {
                        var current = transformed[change.PreviousIndex];
                        transformed.RemoveAt(change.PreviousIndex);
                        transformed.Insert(change.CurrentIndex, current);
                    }

                    break;
                }
                }
            }
        }
Example #2
0
        private IChangeSet <IGroup <TObject, TGroupKey> > Process(ChangeAwareList <IGroup <TObject, TGroupKey> > result, IDictionary <TGroupKey, Group <TObject, TGroupKey> > groupCollection, IChangeSet <ItemWithGroupKey> changes)
        {
            foreach (var grouping in changes.Unified().GroupBy(change => change.Current.Group))
            {
                //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.Group;

                                //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.Refresh:
                            {
                                //1. Check whether item was in the group and should not be now (or vice versa)
                                var currentItem   = change.Current.Item;
                                var previousGroup = change.Current.PreviousGroup.Value;

                                //check whether an item changing has resulted in a different group
                                if (previousGroup.Equals(currentGroup))
                                {
                                    // Propagate refresh event
                                    var cal = (ChangeAwareList <TObject>)list;
                                    cal.Refresh(currentItem);
                                }
                                else
                                {
                                    //add to new group
                                    list.Add(currentItem);

                                    //remove from old group if empty
                                    groupCollection.Lookup(previousGroup)
                                    .IfHasValue(g =>
                                    {
                                        g.Edit(oldList => oldList.Remove(currentItem));
                                        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());
        }
Example #3
0
        private IChangeSet <TValue> Process(Dictionary <TValue, int> values, ChangeAwareList <TValue> result, IChangeSet <ItemWithValue <T, TValue> > changes)
        {
            Action <TValue> addAction = value => values.Lookup(value)
                                        .IfHasValue(count => values[value] = count + 1)
                                        .Else(() =>
            {
                values[value] = 1;
                result.Add(value);
            });

            Action <TValue> removeAction = value =>
            {
                var counter = values.Lookup(value);
                if (!counter.HasValue)
                {
                    return;
                }

                //decrement counter
                var newCount = counter.Value - 1;
                values[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();
                    values.Clear();
                    break;
                }
                }
            }
            return(result.CaptureChanges());
        }
Example #4
0
        public IObservable <IChangeSet <T> > Run()
        {
            return(Observable.Create <IChangeSet <T> >(observer =>
            {
                if (_expireAfter == null && _limitSizeTo < 1)
                {
                    return _source.Scan(new ChangeAwareList <T>(), (state, latest) =>
                    {
                        var items = latest.AsArray();
                        if (items.Length == 1)
                        {
                            state.Add(items);
                        }
                        else
                        {
                            state.AddRange(items);
                        }

                        return state;
                    })
                    .Select(state => state.CaptureChanges())
                    .SubscribeSafe(observer);
                }

                long orderItemWasAdded = -1;
                var locker = new object();

                var sourceList = new ChangeAwareList <ExpirableItem <T> >();

                var sizeLimited = _source.Synchronize(locker)
                                  .Scan(sourceList, (state, latest) =>
                {
                    var items = latest.AsArray();
                    var expirable = items.Select(t => CreateExpirableItem(t, ref orderItemWasAdded));

                    if (items.Length == 1)
                    {
                        sourceList.Add(expirable);
                    }
                    else
                    {
                        sourceList.AddRange(expirable);
                    }

                    if (_limitSizeTo > 0 && state.Count > _limitSizeTo)
                    {
                        //remove oldest items [these will always be the first x in the list]
                        var toRemove = state.Count - _limitSizeTo;
                        state.RemoveRange(0, toRemove);
                    }

                    return state;
                })
                                  .Select(state => state.CaptureChanges())
                                  .Publish();

                var timeLimited = (_expireAfter == null ? Observable.Never <IChangeSet <ExpirableItem <T> > >() : sizeLimited)
                                  .Filter(ei => ei.ExpireAt != DateTime.MaxValue)
                                  .GroupWithImmutableState(ei => ei.ExpireAt)
                                  .MergeMany(grouping =>
                {
                    var expireAt = grouping.Key.Subtract(_scheduler.Now.DateTime);
                    return Observable.Timer(expireAt, _scheduler).Select(_ => grouping);
                })
                                  .Synchronize(locker)
                                  .Select(grouping =>
                {
                    sourceList.RemoveMany(grouping.Items);
                    return sourceList.CaptureChanges();
                });

                var publisher = sizeLimited
                                .Merge(timeLimited)
                                .Cast(ei => ei.Item)
                                .NotEmpty()
                                .SubscribeSafe(observer);

                return new CompositeDisposable(publisher, sizeLimited.Connect());
            }));
        }