Example #1
0
        private static int CountEqual <T> (
            IList <T> listA, IList <T> listB, int startA, int endA, int startB, int endB, DiffComparison update, DiffCache cache)
            where T : IDiffComparable
        {
            int length = 0;

            while (startA < endA && startB < endB)
            {
                var comp = cache.Compare(listA, startA, listB, startB);
                if (comp != DiffComparison.Same && comp != update)
                {
                    break;
                }
                startA++;
                startB++;
                length++;
            }
            return(length);
        }
Example #2
0
        private static IEnumerable <DiffSection <T> > Calculate <T> (
            IList <T> listA, IList <T> listB, int startA, int endA, int startB, int endB, DiffComparison update, DiffCache cache)
            where T : IDiffComparable
        {
            var lcs = FindLongestCommonSubstring(listA, listB, startA, endA, startB, endB, update, cache);

            if (lcs.Success)
            {
                // deal with the section before
                var sectionsBefore =
                    Calculate(listA, listB, startA, lcs.PositionA, startB, lcs.PositionB, update, cache);

                foreach (var section in sectionsBefore)
                {
                    yield return(section);
                }

                // output the copy operation
                for (int i = 0; i < lcs.Length; i++)
                {
                    int indexA = lcs.PositionA + i, indexB = lcs.PositionB + i;
                    yield return(new DiffSection <T> (DiffType.Copy, indexA, listA [indexA], indexB, listB [indexB]));
                }

                // deal with the section after
                var sectionsAfter =
                    Calculate(listA, listB, lcs.PositionA + lcs.Length, endA, lcs.PositionB + lcs.Length, endB, update, cache);

                foreach (var section in sectionsAfter)
                {
                    yield return(section);
                }

                yield break;
            }
            else if (update == DiffComparison.SoftUpdate)
            {
                var sectionsUpdate = Calculate(listA, listB, startA, endA, startB, endB, DiffComparison.Update, cache);
                foreach (var section in sectionsUpdate)
                {
                    yield return(section);
                }
                yield break;
            }

            // if we get here, no LCS
            if (endA > startA)
            {
                // we got content from first collection --> deleted
                for (int i = 0; i < endA - startA; i++)
                {
                    yield return(new DiffSection <T> (DiffType.Remove, startA + i, listA[startA + i], startB, default(T)));
                }
            }

            if (endB > startB)
            {
                // we got content from second collection --> inserted
                for (int i = 0; i < endB - startB; i++)
                {
                    yield return(new DiffSection <T> (DiffType.Add, startA, default(T), startB + i, listB[startB + i]));
                }
            }
        }
Example #3
0
        private static LongestCommonSubstringResult FindLongestCommonSubstring <T> (
            IList <T> listA, IList <T> listB, int startA, int endA, int startB, int endB, DiffComparison update, DiffCache cache)
            where T : IDiffComparable
        {
            // default result, if we can't find anything
            var result = new LongestCommonSubstringResult();

            for (int index1 = startA; index1 < endA; index1++)
            {
                for (int index2 = startB; index2 < endB; index2++)
                {
                    int length = CountEqual(listA, listB, index1, endA, index2, endB, update, cache);
                    if (length > 0)
                    {
                        // Is longer than what we already have? --> record new LCS
                        if (length > result.Length)
                        {
                            result = new LongestCommonSubstringResult(index1, index2, length);
                        }
                        break;
                    }
                }
            }

            return(result);
        }
Example #4
0
        /// <summary>
        /// Calculates move and replace operation (besides add and remove) and adjusts indices
        /// so events can be applied sequentially (included moves) to the source list
        /// </summary>
        public static IList <DiffSection <T> > Calculate <T> (IList <T> listA, IList <T> listB)
            where T : IDiffComparable
        {
            var cache = new DiffCache();
            var diffs = Calculate(listA, listB, 0, listA.Count, 0, listB.Count, DiffComparison.SoftUpdate, cache);

            var diffsWithReplace = diffs.Select(x => {
                if (x.Type == DiffType.Copy && cache.Compare(listA, x.OldIndex, listB, x.NewIndex) != DiffComparison.Same)
                {
                    x.Type = DiffType.Replace;
                }
                return(x);
            });

            var diffsDic = diffsWithReplace
                           .Where(x => x.Type != DiffType.Copy)
                           .GroupBy(diff => diff.Type)
                           .ToDictionary(gr => gr.Key, gr => gr.ToList());

            // Calculate Move diffs
            if (diffsDic.ContainsKey(DiffType.Add) && diffsDic.ContainsKey(DiffType.Remove))
            {
                foreach (var addDiff in diffsDic[DiffType.Add])
                {
                    var rmDiff = diffsDic [DiffType.Remove].FirstOrDefault(x =>
                                                                           cache.Compare(listA, x.OldIndex, listB, addDiff.NewIndex) == DiffComparison.Update);

                    if (rmDiff != null)
                    {
                        addDiff.Link     = rmDiff;
                        rmDiff.Link      = addDiff;
                        addDiff.OldIndex = rmDiff.NewIndex;
                        rmDiff.Move      = addDiff.Move = rmDiff.NewIndex < addDiff.NewIndex
                                                     ? DiffMove.Forward : DiffMove.Backward;
                    }
                }
            }

            var fwOffset    = 0;
            var bwOffsetDic = new Dictionary <DiffSection <T>, int> ();

            return(diffsDic
                   .SelectMany(x => x.Value)
                   .OrderBy(x => x.NewIndex)

                   // Deletes must happen before inserts in the same position to prevent problems with indices
                   .ThenBy(x => x.Type == DiffType.Remove ? -1 : 0)

                   // Add offset to indices so events can be raised linearly without conflicting with moves
                   .Select(x => {
                if (x.Type == DiffType.Add)
                {
                    if (x.IsMove)
                    {
                        if (x.Move == DiffMove.Forward)
                        {
                            fwOffset--;
                        }
                        else
                        {
                            bwOffsetDic.Add(x.Link, 0);
                        }
                        x.Link = null;  // Reference is not needed any more
                        x.Type = DiffType.Move;
                    }
                    foreach (var k in bwOffsetDic.Keys.ToList())
                    {
                        bwOffsetDic[k] -= 1;
                    }
                }
                else if (x.Type == DiffType.Remove)
                {
                    if (!x.IsMove)
                    {
                        foreach (var k in bwOffsetDic.Keys.ToList())
                        {
                            bwOffsetDic[k] += 1;
                        }
                    }
                    else if (x.Move == DiffMove.Forward)
                    {
                        fwOffset++;
                    }
                    else     // Move == Diff.Backward
                    {
                        x.Link.OldIndex += bwOffsetDic[x];
                        bwOffsetDic.Remove(x);
                    }
                }
                x.NewIndex += fwOffset;
                return x;
            })
                   .Where(x => !(x.Type == DiffType.Remove && x.IsMove))
                   .ToList());
        }