예제 #1
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());
        }
예제 #2
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());
        }
예제 #3
0
        private void Requery(Func <T, bool> predicate, List <ItemWithMatch> all, ChangeAwareList <ItemWithMatch> filtered)
        {
            if (_policy == ListFilterPolicy.ClearAndReplace)
            {
                var newMatches = all.Where(iwm => predicate(iwm.Item)).ToList();

                //mark items as matched?
                filtered.Clear();
                filtered.AddRange(newMatches);

                //reset state
                all.Where(iwm => iwm.IsMatch).ForEach(iwm => iwm.IsMatch = false);
                newMatches.ForEach(iwm => iwm.IsMatch = true);

                return;
            }


            var mutatedMatches = new List <Action>(all.Count);

            var newState = all.Select(item =>
            {
                var match    = predicate(item.Item);
                var wasMatch = item.IsMatch;

                //Mutate match - defer until filtered list has been modified
                //[to prevent potential IndexOf failures]
                if (item.IsMatch != match)
                {
                    mutatedMatches.Add(() => 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);

            filtered.RemoveMany(noLongerMatched);

            //reflect new matches in the list
            var newMatched = newState.Where(state => state.IsMatch && !state.WasMatch).Select(state => state.Item);

            filtered.AddRange(newMatched);

            //finally apply mutations
            mutatedMatches.ForEach(m => m());
        }
예제 #4
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());
        }
예제 #5
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());
        }
예제 #6
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());
        }
예제 #7
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());
        }
예제 #8
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());
        }