예제 #1
0
        private IChangeSet <T> Page(IChangeSet <T> changeset = null)
        {
            if (changeset != null)
            {
                _all.Clone(changeset);
            }

            var previous = _paged;

            int pages = CalculatePages();
            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());
        }
예제 #2
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());
        }
        public IList <Change <TObject, TKey> > Calculate(IKeyValueCollection <TObject, TKey> currentItems,
                                                         IKeyValueCollection <TObject, TKey> previousItems, IChangeSet <TObject, TKey> sourceUpdates)
        {
            if (currentItems.SortReason == SortReason.ComparerChanged)
            {
                //clear collection and rebuild
                var removed  = previousItems.Select((item, index) => new Change <TObject, TKey>(ChangeReason.Remove, item.Key, item.Value, index));
                var newitems = currentItems.Select((item, index) => new Change <TObject, TKey>(ChangeReason.Add, item.Key, item.Value, index));

                return(new List <Change <TObject, TKey> >(removed.Union(newitems)));
            }

            var previousList = previousItems.ToList();
            var keyComparer  = new KeyComparer <TObject, TKey>();

            var removes    = previousItems.Except(currentItems, keyComparer).ToList();
            var adds       = currentItems.Except(previousItems, keyComparer).ToList();
            var inbothKeys = previousItems.Intersect(currentItems, keyComparer)
                             .Select(x => x.Key).ToHashSet();

            var result = new List <Change <TObject, TKey> >();

            foreach (var remove in removes)
            {
                int index = previousList.IndexOf(remove);

                previousList.RemoveAt(index);
                result.Add(new Change <TObject, TKey>(ChangeReason.Remove, remove.Key, remove.Value, index));
            }

            foreach (var add in adds)
            {
                //find new insert position
                int index       = previousList.BinarySearch(add, currentItems.Comparer);
                int insertIndex = ~index;
                previousList.Insert(insertIndex, add);
                result.Add(new Change <TObject, TKey>(ChangeReason.Add, add.Key, add.Value, insertIndex));
            }

            //Adds and removes ahave been accounted for
            //so check whether anything in the remaining change set have been moved ot updated
            var remainingItems = sourceUpdates
                                 .EmptyIfNull()
                                 .Where(u => inbothKeys.Contains(u.Key) &&
                                        (u.Reason == ChangeReason.Update ||
                                         u.Reason == ChangeReason.Moved ||
                                         u.Reason == ChangeReason.Evaluate))
                                 .ToList();

            foreach (var change in remainingItems)
            {
                if (change.Reason == ChangeReason.Update)
                {
                    var current  = new KeyValuePair <TKey, TObject>(change.Key, change.Current);
                    var previous = new KeyValuePair <TKey, TObject>(change.Key, change.Previous.Value);

                    //remove from the actual index
                    var removeIndex = previousList.IndexOf(previous);
                    previousList.RemoveAt(removeIndex);

                    //insert into the desired index
                    int desiredIndex = previousList.BinarySearch(current, currentItems.Comparer);
                    int insertIndex  = ~desiredIndex;
                    previousList.Insert(insertIndex, current);

                    result.Add(new Change <TObject, TKey>(ChangeReason.Update, current.Key, current.Value,
                                                          previous.Value, insertIndex, removeIndex));
                }
                else if (change.Reason == ChangeReason.Moved)
                {
                    //TODO:  We have the index already, would be more efficient to calculate new position from the original index
                    var current = new KeyValuePair <TKey, TObject>(change.Key, change.Current);

                    var previousindex = previousList.IndexOf(current);
                    int desiredIndex  = previousList.BinarySearch(current, currentItems.Comparer);
                    int insertIndex   = ~desiredIndex;

                    //this should never be the case, but check anyway
                    if (previousindex == insertIndex)
                    {
                        continue;
                    }


                    if (insertIndex < 0)
                    {
                        throw new SortException("Cannot determine current index");
                    }

                    previousList.RemoveAt(previousindex);
                    previousList.Insert(insertIndex, current);
                    result.Add(new Change <TObject, TKey>(current.Key, current.Value, insertIndex, previousindex));
                }
                else
                {
                    //TODO: re-evaluate to check whether item should be moved
                    result.Add(change);
                }
            }

            //Alternative to evaluate is to check order
            var evaluates = remainingItems.Where(c => c.Reason == ChangeReason.Evaluate)
                            .OrderByDescending(x => new KeyValuePair <TKey, TObject>(x.Key, x.Current), currentItems.Comparer)
                            .ToList();

            //calculate moves.  Very expensive operation
            //TODO: Try and make this better
            foreach (var u in evaluates)
            {
                var current = new KeyValuePair <TKey, TObject>(u.Key, u.Current);
                var old     = previousList.IndexOf(current);

                if (old == -1)
                {
                    continue;
                }

                int newposition = GetInsertPositionLinear(previousList, current, currentItems.Comparer);

                if (old < newposition)
                {
                    newposition--;
                }

                if (old == newposition)
                {
                    continue;
                }

                previousList.RemoveAt(old);
                previousList.Insert(newposition, current);
                result.Add(new Change <TObject, TKey>(u.Key, u.Current, newposition, old));
            }
            return(result);
        }
예제 #4
0
        private static PageChangeSet <T> Page(List <T> all, ChangeAwareList <T> paged, IPageRequest request, IChangeSet <T>?changeSet = null)
        {
            if (changeSet is not null)
            {
                all.Clone(changeSet);
            }

            var previous = paged;

            int pages = CalculatePages(all, request);
            int page  = request.Page > pages ? pages : request.Page;
            int skip  = request.Size * (page - 1);

            var current = all.Skip(skip)
                          .Take(request.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 + request.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);
            }

            var changed = paged.CaptureChanges();

            return(new PageChangeSet <T>(changed, new PageResponse(paged.Count, page, all.Count, pages)));
        }