Beispiel #1
0
        public IChangeSet <T> Write(IChangeSet <T> changes)
        {
            if (changes == null)
            {
                throw new ArgumentNullException(nameof(changes));
            }
            IChangeSet <T> result;

            lock (_locker)
            {
                _data.Clone(changes);
                result = _data.CaptureChanges();
            }
            return(result);
        }
Beispiel #2
0
        private IObservable <IChangeSet <TDestination> > CreateWithChangeset()
        {
            return(Observable.Create <IChangeSet <TDestination> >(observer =>
            {
                var result = new ChangeAwareList <TDestination>();

                var transformed = _source.Transform(t =>
                {
                    var locker = new object();
                    var collection = _manyselector(t);
                    var changes = _childChanges(t).Synchronize(locker).Skip(1);
                    return new ManyContainer(collection, changes);
                })
                                  .Publish();

                var outerLock = new object();
                var intial = transformed
                             .Synchronize(outerLock)
                             .Select(changes => new ChangeSet <TDestination>(new DestinationEnumerator(changes, _equalityComparer)));

                var subsequent = transformed
                                 .MergeMany(x => x.Changes)
                                 .Synchronize(outerLock);

                var allChanges = intial.Merge(subsequent).Select(changes =>
                {
                    result.Clone(changes);
                    return result.CaptureChanges();
                });

                return new CompositeDisposable(allChanges.SubscribeSafe(observer), transformed.Connect());
            }));
        }
        public IObservable <IChangeSet <TDestination> > Run()
        {
            if (_childChanges != null)
            {
                return(CreateWithChangeset());
            }

            return(Observable.Create <IChangeSet <TDestination> >(observer =>
            {
                //NB: ChangeAwareList is used internally by dd to capture changes to a list and ensure they can be replayed by subsequent operators
                var result = new ChangeAwareList <TDestination>();

                return _source.Transform(item => new ManyContainer(_manyselector(item).ToArray()), true)
                .Select(changes =>
                {
                    var destinationChanges = new ChangeSet <TDestination>(new DestinationEnumerator(changes, _equalityComparer));
                    result.Clone(destinationChanges, _equalityComparer);
                    return result.CaptureChanges();
                })

                .NotEmpty()
                .SubscribeSafe(observer);
            }
                                                                  ));
        }
Beispiel #4
0
        private IChangeSet <IGrouping <TObject, TGroupKey> > CreateChangeSet(ChangeAwareList <IGrouping <TObject, TGroupKey> > result, IDictionary <TGroupKey, GroupContainer> allGroupings, IDictionary <TGroupKey, IGrouping <TObject, TGroupKey> > initialStateOfGroups)
        {
            //Now maintain target list
            foreach (var intialGroup in initialStateOfGroups)
            {
                var key     = intialGroup.Key;
                var current = allGroupings[intialGroup.Key];

                if (current.List.Count == 0)
                {
                    //remove if empty
                    allGroupings.Remove(key);
                    result.Remove(intialGroup.Value);
                }
                else
                {
                    var currentState = GetGroupState(current);
                    if (intialGroup.Value.Count == 0)
                    {
                        //an add
                        result.Add(currentState);
                    }
                    else
                    {
                        //a replace (or add if the old group has already been removed)
                        result.Replace(intialGroup.Value, currentState);
                    }
                }
            }
            return(result.CaptureChanges());
        }
        /// <summary>
        /// Convert a binding list into an observable change set.
        /// Change set observes list change events.
        /// </summary>
        /// <typeparam name="TCollection">The collection type.</typeparam>
        /// <typeparam name="T">The type of the object.</typeparam>
        /// <param name="source">The source.</param>
        /// <returns>An observable which emits change set values.</returns>
        /// <exception cref="System.ArgumentNullException">source.</exception>
        public static IObservable <IChangeSet <T> > ToObservableChangeSet <TCollection, T>(this TCollection source)
            where TCollection : IBindingList, IEnumerable <T>
        {
            if (source is null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            return(Observable.Create <IChangeSet <T> >(
                       observer =>
            {
                var data = new ChangeAwareList <T>(source);

                if (data.Count > 0)
                {
                    observer.OnNext(data.CaptureChanges());
                }

                return source.ObserveCollectionChanges().Scan(
                    data,
                    (list, args) =>
                {
                    var changes = args.EventArgs;

                    switch (changes.ListChangedType)
                    {
                    case ListChangedType.ItemAdded when source[changes.NewIndex] is T newItem:
                        {
                            list.Add(newItem);
                            break;
                        }
Beispiel #6
0
        public IObservable <IChangeSet <T> > Run()
        {
            return(Observable.Create <IChangeSet <T> >(observer =>
            {
                var locker = new object();

                Func <T, bool> predicate = t => false;
                var all = new List <ItemWithMatch>();
                var filtered = new ChangeAwareList <ItemWithMatch>();

                //requery when predicate changes
                var predicateChanged = _predicates
                                       .Synchronize(locker)
                                       .Select(newPredicate =>
                {
                    predicate = newPredicate;
                    Requery(predicate, all, filtered);
                    return filtered.CaptureChanges();
                });

                /*
                 * Apply the transform operator so 'IsMatch' state can be evalutated and captured one time only
                 * This is to eliminate the need to re-apply the predicate when determining whether an item was previously matched,
                 * which is essential when we have mutable state
                 */

                //Need to get item by index and store it in the transform
                var filteredResult = _source
                                     .Synchronize(locker)
                                     .Transform <T, ItemWithMatch>((t, previous, idx) =>
                {
                    var wasMatch = previous.ConvertOr(p => p.IsMatch, () => false);
                    return new ItemWithMatch(t, idx, predicate(t), wasMatch);
                }, true)
                                     .Select(changes =>
                {
                    all.Clone(changes);     //keep track of all changes
                    Process(filtered, changes);
                    return filtered.CaptureChanges();
                });

                return predicateChanged.Merge(filteredResult)
                .NotEmpty()
                .Select(changes => changes.Transform(iwm => iwm.Item))             // use convert, not transform
                .SubscribeSafe(observer);
            }));
        }
        public IObservable <IChangeSet <T> > Run()
        {
            return(Observable.Create <IChangeSet <T> >(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());
            }));
        }
Beispiel #8
0
        private IChangeSet <ItemWithMatch> Requery(Func <T, bool> predicate, List <ItemWithMatch> all, ChangeAwareList <ItemWithMatch> filtered)
        {
            if (all.Count == 0)
            {
                return(ChangeSet <ItemWithMatch> .Empty);
            }

            if (_policy == ListFilterPolicy.ClearAndReplace)
            {
                var itemsWithMatch = all.Select(iwm => new ItemWithMatch(iwm.Item, predicate(iwm.Item), iwm.IsMatch)).ToList();

                //mark items as matched?
                filtered.Clear();
                filtered.AddRange(itemsWithMatch.Where(iwm => iwm.IsMatch));

                //reset state for all items
                all.Clear();
                all.AddRange(itemsWithMatch);
                return(filtered.CaptureChanges());
            }

            var toAdd    = new List <ItemWithMatch>(all.Count);
            var toRemove = new List <ItemWithMatch>(all.Count);

            for (int i = 0; i < all.Count; i++)
            {
                var original = all[i];

                var newItem = new ItemWithMatch(original.Item, predicate(original.Item), original.IsMatch);
                all[i] = newItem;

                if (newItem.IsMatch && !newItem.WasMatch)
                {
                    toAdd.Add(newItem);
                }
                else if (!newItem.IsMatch && newItem.WasMatch)
                {
                    toRemove.Add(newItem);
                }
            }

            filtered.RemoveMany(toRemove);
            filtered.AddRange(toAdd);

            return(filtered.CaptureChanges());
        }
Beispiel #9
0
        private IChangeSet <T> Reset(List <T> original, ChangeAwareList <T> target)
        {
            var sorted = original.OrderBy(t => t, _comparer).ToList();

            target.Clear();
            target.AddRange(sorted);
            return(target.CaptureChanges());
        }
Beispiel #10
0
        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());
        }
Beispiel #11
0
        /// <summary>
        /// Convert a binding list into an observable change set
        /// </summary>
        /// <typeparam name="T">The type of the object.</typeparam>
        /// <typeparam name="TCollection"></typeparam>
        /// <param name="source">The source.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentNullException">source</exception>
        public static IObservable <IChangeSet <T> > ToObservableChangeSet <TCollection, T>(this TCollection source)
            where TCollection : IBindingList, IEnumerable <T>
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            return(Observable.Create <IChangeSet <T> >(observer =>
            {
                var data = new ChangeAwareList <T>(source);

                if (data.Count > 0)
                {
                    observer.OnNext(data.CaptureChanges());
                }

                return source.ObserveCollectionChanges()
                .Scan(data, (list, args) =>
                {
                    var changes = args.EventArgs;

                    switch (changes.ListChangedType)
                    {
                    case ListChangedType.ItemAdded:
                        {
                            list.Add((T)source[changes.NewIndex]);
                            break;
                        }

                    case ListChangedType.ItemDeleted:
                        {
                            list.RemoveAt(changes.NewIndex);
                            break;
                        }

                    case ListChangedType.ItemChanged:
                        {
                            list[changes.NewIndex] = (T)source[changes.NewIndex];
                            break;
                        }

                    case ListChangedType.Reset:
                        {
                            list.Clear();
                            list.AddRange(source);
                            break;
                        }
                    }

                    return list;
                })
                .Select(list => list.CaptureChanges())
                .SubscribeSafe(observer);
            }));
        }
        public IChangeSet <T> Write(IChangeSet <T> changes)
        {
            if (changes == null)
            {
                throw new ArgumentNullException(nameof(changes));
            }
            IChangeSet <T> result;

            _lock.EnterWriteLock();
            try
            {
                _data.Clone(changes);
                result = _data.CaptureChanges();
            }
            finally
            {
                _lock.ExitWriteLock();
            }
            return(result);
        }
Beispiel #13
0
        private static 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());
        }
Beispiel #14
0
        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));
        }
Beispiel #15
0
        public IObservable <IChangeSet <T> > Run()
        {
            return(Observable.Create <IChangeSet <T> >(observer =>
            {
                var locker = new object();

                Func <T, bool> predicate = t => false;

                var all = new List <ItemWithMatch>();
                var filtered = new ChangeAwareList <ItemWithMatch>();


                //requery when predicate changes
                var predicateChanged = _predicates.Synchronize(locker)
                                       .Select(newPredicate =>
                {
                    predicate = newPredicate;
                    Requery(predicate, all, filtered);
                    return filtered.CaptureChanges();
                });

                /*
                 * Apply the transform operator so 'IsMatch' state can be evalutated and captured one time only
                 * This is to eliminate the need to re-apply the predicate when determining whether an item was previously matched
                 */
                var filteredResult = _source.Synchronize(locker)
                                     .Transform(t => new ItemWithMatch(t, predicate(t)))
                                     .Select(changes =>
                {
                    all.Clone(changes);                           //keep track of all changes
                    filtered.Filter(changes, iwm => iwm.IsMatch); //maintain filtered result
                    return filtered.CaptureChanges();
                });

                return predicateChanged.Merge(filteredResult)
                .NotEmpty()
                .Select(changes => changes.Transform(iwm => iwm.Item))
                .SubscribeSafe(observer);
            }));
        }
Beispiel #16
0
        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));
        }
Beispiel #17
0
        private IChangeSet <T> ChangeComparer(ChangeAwareList <T> target, IComparer <T> comparer)
        {
            _comparer = comparer;
            if (_resetThreshold > 0 && target.Count <= _resetThreshold)
            {
                return(Reorder(target));
            }

            var sorted = target.OrderBy(t => t, _comparer).ToList();

            target.Clear();
            target.AddRange(sorted);
            return(target.CaptureChanges());
        }
Beispiel #18
0
        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());
        }
Beispiel #19
0
        /// <summary>
        /// Convert an observable collection into an observable change set.
        /// Change set observes collection change events.
        /// </summary>
        /// <typeparam name="TCollection">The type of collection.</typeparam>
        /// <typeparam name="T">The type of the object.</typeparam>
        /// <param name="source">The source.</param>
        /// <returns>An observable that emits the change set.</returns>
        /// <exception cref="System.ArgumentNullException">source.</exception>
        public static IObservable <IChangeSet <T> > ToObservableChangeSet <TCollection, T>(this TCollection source)
            where TCollection : INotifyCollectionChanged, IEnumerable <T>
        {
            if (source is null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            return(Observable.Create <IChangeSet <T> >(
                       observer =>
            {
                var data = new ChangeAwareList <T>(source);

                if (data.Count > 0)
                {
                    observer.OnNext(data.CaptureChanges());
                }

                return source.ObserveCollectionChanges().Scan(
                    data,
                    (list, args) =>
                {
                    var changes = args.EventArgs;

                    switch (changes.Action)
                    {
                    case NotifyCollectionChangedAction.Add when changes.NewItems is not null:
                        {
                            if (changes.NewItems.Count == 1 && changes.NewItems[0] is T item)
                            {
                                list.Insert(changes.NewStartingIndex, item);
                            }
                            else
                            {
                                list.InsertRange(changes.NewItems.Cast <T>(), changes.NewStartingIndex);
                            }

                            break;
                        }
Beispiel #20
0
        private IChangeSet <T> Reorder(ChangeAwareList <T> target)
        {
            int index = -1;

            foreach (var item in target.OrderBy(t => t, _comparer).ToList())
            {
                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());
        }
        /// <summary>
        /// Convert an observable collection into an observable change set
        /// </summary>
        /// <typeparam name="T">The type of the object.</typeparam>
        /// <typeparam name="TCollection"></typeparam>
        /// <param name="source">The source.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentNullException">source</exception>
        public static IObservable <IChangeSet <T> > ToObservableChangeSet <TCollection, T>(this TCollection source)
            where TCollection : INotifyCollectionChanged, IEnumerable <T>
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            return(Observable.Create <IChangeSet <T> >(observer =>
            {
                var data = new ChangeAwareList <T>(source);

                if (data.Count > 0)
                {
                    observer.OnNext(data.CaptureChanges());
                }

                return source.ObserveCollectionChanges()
                .Scan(data, (list, args) =>
                {
                    var changes = args.EventArgs;

                    switch (changes.Action)
                    {
                    case NotifyCollectionChangedAction.Add:
                        {
                            if (changes.NewItems.Count == 1)
                            {
                                list.Insert(changes.NewStartingIndex, (T)changes.NewItems[0]);
                            }
                            else
                            {
                                list.InsertRange(changes.NewItems.Cast <T>(), changes.NewStartingIndex);
                            }
                            break;
                        }

                    case NotifyCollectionChangedAction.Remove:
                        {
                            if (changes.OldItems.Count == 1)
                            {
                                list.RemoveAt(changes.OldStartingIndex);
                            }
                            else
                            {
                                list.RemoveRange(changes.OldStartingIndex, changes.OldItems.Count);
                            }
                            break;
                        }

                    case NotifyCollectionChangedAction.Replace:
                        {
                            list[changes.NewStartingIndex] = (T)changes.NewItems[0];
                            break;
                        }

                    case NotifyCollectionChangedAction.Reset:
                        {
                            list.Clear();
                            list.AddRange(source);
                            break;
                        }

                    case NotifyCollectionChangedAction.Move:
                        {
                            list.Move(changes.OldStartingIndex, changes.NewStartingIndex);
                            break;
                        }
                    }
                    return list;
                })
                .Select(list => list.CaptureChanges())
                .SubscribeSafe(observer);
            }));
        }
Beispiel #22
0
        private IChangeSet <T> ProcessImpl(ChangeAwareList <T> target, IChangeSet <T> changes)
        {
            var refreshes = new List <T>(changes.Refreshes);

            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.Remove:
                {
                    var current = change.Item.Current;
                    Remove(target, current);
                    break;
                }

                case ListChangeReason.Refresh:
                {
                    // add to refresh list so position can be calculated
                    refreshes.Add(change.Item.Current);

                    // add to current list so downstream operators can receive a refresh
                    // notification, so get the latest index and pass the index up the chain
                    var indexed = target.IndexOfOptional(change.Item.Current).ValueOrThrow(() => new SortException($"Cannot find index of {typeof(T).Name} -> {change.Item.Current}. Expected to be in the list"));

                    target.Refresh(indexed.Item, indexed.Index);
                    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.RemoveRange:
                {
                    target.RemoveMany(change.Range);
                    break;
                }

                case ListChangeReason.Clear:
                {
                    target.Clear();
                    break;
                }
                }
            }

            // Now deal with refreshes [can be expensive]
            foreach (var item in refreshes)
            {
                var old = target.IndexOf(item);
                if (old == -1)
                {
                    continue;
                }

                int newPosition = GetInsertPositionLinear(target, item);
                if (old < newPosition)
                {
                    newPosition--;
                }

                if (old == newPosition)
                {
                    continue;
                }

                target.Move(old, newPosition);
            }

            return(target.CaptureChanges());
        }
Beispiel #23
0
        private IChangeSet <T> ProcessImpl(ChangeAwareList <T> target, IChangeSet <T> changes)
        {
            if (_comparer == null)
            {
                target.Clone(changes);
                return(target.CaptureChanges());
            }

            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());
        }
Beispiel #24
0
        private IChangeSet <ItemWithMatch> Process(ChangeAwareList <ItemWithMatch> filtered, IChangeSet <ItemWithMatch> changes)
        {
            //Maintain all items as well as filtered list. This enables us to a) requery when the predicate changes b) check the previous state when Refresh is called
            foreach (var item in changes)
            {
                switch (item.Reason)
                {
                case ListChangeReason.Add:
                {
                    var change = item.Item;
                    if (change.Current.IsMatch)
                    {
                        filtered.Add(change.Current);
                    }
                    break;
                }

                case ListChangeReason.AddRange:
                {
                    var matches = item.Range.Where(t => t.IsMatch).ToList();
                    filtered.AddRange(matches);
                    break;
                }

                case ListChangeReason.Replace:
                {
                    var change   = item.Item;
                    var match    = change.Current.IsMatch;
                    var wasMatch = item.Item.Current.WasMatch;
                    if (match)
                    {
                        if (wasMatch)
                        {
                            //an update, so get the latest index and pass the index up the chain
                            var previous = filtered.Select(x => x.Item)
                                           .IndexOfOptional(change.Previous.Value.Item)
                                           .ValueOrThrow(() => new InvalidOperationException($"Cannot find index of {typeof(T).Name} -> {change.Previous.Value}. Expected to be in the list"));

                            //replace inline
                            filtered[previous.Index] = change.Current;
                        }
                        else
                        {
                            filtered.Add(change.Current);
                        }
                    }
                    else
                    {
                        if (wasMatch)
                        {
                            filtered.Remove(change.Previous.Value);
                        }
                    }
                    break;
                }

                case ListChangeReason.Refresh:
                {
                    var change   = item.Item;
                    var match    = change.Current.IsMatch;
                    var wasMatch = item.Item.Current.WasMatch;
                    if (match)
                    {
                        if (wasMatch)
                        {
                            //an update, so get the latest index and pass the index up the chain
                            var previous = filtered.Select(x => x.Item)
                                           .IndexOfOptional(change.Current.Item)
                                           .ValueOrThrow(() => new InvalidOperationException($"Cannot find index of {typeof(T).Name} -> {change.Previous.Value}. Expected to be in the list"));

                            filtered.RefreshAt(previous.Index);
                        }
                        else
                        {
                            filtered.Add(change.Current);
                        }
                    }
                    else
                    {
                        if (wasMatch)
                        {
                            filtered.Remove(change.Current);
                        }
                    }
                    break;
                }

                case ListChangeReason.Remove:
                {
                    filtered.Remove(item.Item.Current);
                    break;
                }

                case ListChangeReason.RemoveRange:
                {
                    filtered.RemoveMany(item.Range);
                    break;
                }

                case ListChangeReason.Clear:
                {
                    filtered.ClearOrRemoveMany(item);
                    break;
                }
                }
            }
            return(filtered.CaptureChanges());
        }
        public void Add()
        {
            _source.Add(1);

            var changes = _source.CaptureChanges();

            _clone.Clone(changes);

            //assert collection
            _clone.Should().BeEquivalentTo(_source);
        }
Beispiel #26
0
        public void Add()
        {
            _list.Add(1);

            //assert changes
            var changes = _list.CaptureChanges();

            changes.Count.Should().Be(1);
            changes.Adds.Should().Be(1);
            changes.First().Item.Current.Should().Be(1);

            //assert collection
            _list.ShouldAllBeEquivalentTo(Enumerable.Range(1, 1));
        }
        public void Add()
        {
            _source.Add(1);

            var changes = _source.CaptureChanges();

            _clone.Clone(changes);

            //assert collection
            CollectionAssert.AreEqual(_source, _clone);
        }
Beispiel #28
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, ReferenceEqualityComparer <TObject> .Instance);
                                    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.PrevousGroup.Value;

                                //check whether an item changing has resulted in a different group
                                if (previousGroup.Equals(currentGroup))
                                {
                                    // Propagate refresh eventt
                                    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());
        }
Beispiel #29
0
        private static IChangeSet <TValue> Process(Dictionary <TValue, int> values, ChangeAwareList <TValue> result, IChangeSet <ItemWithMatch> changes)
        {
            void AddAction(TValue value) => values.Lookup(value)
            .IfHasValue(count => values[value] = count + 1)
            .Else(() =>
            {
                values[value] = 1;
                result.Add(value);
            });

            void RemoveAction(TValue 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);
                values.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.Refresh:
                {
                    var value    = change.Item.Current.Value;
                    var previous = change.Item.Current.Previous;
                    if (value.Equals(previous))
                    {
                        continue;
                    }

                    RemoveAction(previous);
                    AddAction(value);
                    break;
                }

                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());
        }
        public void Add()
        {
            _list.Add(1);

            //assert changes
            var changes = _list.CaptureChanges();

            Assert.AreEqual(1, changes.Count);
            Assert.AreEqual(1, changes.Adds);
            Assert.AreEqual(1, changes.First().Item.Current);

            //assert collection
            CollectionAssert.AreEqual(Enumerable.Range(1, 1), _list);
        }