Ejemplo n.º 1
0
        /// <summary>
        /// Mutate the specified list to have the same elements as another list, by inserting or removing as needed. The end result is that
        /// <paramref name="target"/> will have equivalent elements as <paramref name="source"/>, in the same order and positions.
        /// </summary>
        /// <typeparam name="T">
        /// The type of elements in the lists.
        /// </typeparam>
        /// <param name="target">
        /// The list to mutate. Elements will possibly be inserted into or deleted from this list.
        /// </param>
        /// <param name="source">
        /// The list to use as the source of mutations for <paramref name="target"/>.
        /// </param>
        /// <param name="options">
        /// A <see cref="DiffOptions"/> object specifying options to the diff algorithm, or <c>null</c> if defaults should be used.
        /// </param>
        /// <param name="comparer">
        /// The optional <see cref="IEqualityComparer{T}"/> to use when comparing elements.
        /// If not specified/<c>null</c>, <see cref="EqualityComparer{T}.Default"/> will be used.
        /// </param>
        /// <param name="aligner">
        /// The <see cref="IDiffElementAligner{T}"/> to use when aligning elements.
        /// If not specified/<c>null</c>, <see cref="BasicReplaceInsertDeleteDiffElementAligner{T}"/> will be used.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <para><paramref name="target"/> is <c>null</c>.</para>
        /// <para>- or -</para>
        /// <para><paramref name="source"/> is <c>null</c>.</para>
        /// </exception>
        /// <remarks>
        /// The main purpose of this method is to avoid clearing and refilling the list from scratch and instead
        /// make adjustments to it to have the right elements. Useful in conjunction with UI bindings and
        /// similar that react to changes to the list.
        /// </remarks>
        public static void MutateToBeLike <T>([NotNull] this IList <T> target, [NotNull] IList <T> source, [CanBeNull] DiffOptions options, [CanBeNull] IEqualityComparer <T> comparer = null, [CanBeNull] IDiffElementAligner <T> aligner = null)
        {
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            options  = options ?? new DiffOptions();
            comparer = comparer ?? EqualityComparer <T> .Default;
            aligner  = aligner ?? new BasicReplaceInsertDeleteDiffElementAligner <T>();

            var sections = Diff.CalculateSections(target, source, options, comparer);
            var items    = Diff.AlignElements(target, source, sections, aligner).ToList();

            Assume.That(items != null);

            int targetIndex = 0;

            foreach (var item in items)
            {
                switch (item.Operation)
                {
                case DiffOperation.Match:
                    targetIndex++;
                    break;

                case DiffOperation.Insert:
                    target.Insert(targetIndex, item.ElementFromCollection2.Value);
                    targetIndex++;
                    break;

                case DiffOperation.Delete:
                    target.RemoveAt(targetIndex);
                    break;

                case DiffOperation.Replace:
                case DiffOperation.Modify:
                    target[targetIndex] = item.ElementFromCollection2.Value;
                    targetIndex++;
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }
        }
Ejemplo n.º 2
0
        public Merge([NotNull] IList <T> commonBase, [NotNull] IList <T> left, [NotNull] IList <T> right, [NotNull] IDiffElementAligner <T> aligner, [NotNull] IMergeConflictResolver <T> conflictResolver, [NotNull] IEqualityComparer <T> comparer, [NotNull] DiffOptions diffOptions)
        {
            if (commonBase == null)
            {
                throw new ArgumentNullException(nameof(commonBase));
            }
            if (left == null)
            {
                throw new ArgumentNullException(nameof(left));
            }
            if (right == null)
            {
                throw new ArgumentNullException(nameof(right));
            }
            if (aligner == null)
            {
                throw new ArgumentNullException(nameof(aligner));
            }
            if (comparer == null)
            {
                throw new ArgumentNullException(nameof(comparer));
            }
            if (diffOptions == null)
            {
                throw new ArgumentNullException(nameof(diffOptions));
            }

            _ConflictResolver = conflictResolver ?? throw new ArgumentNullException(nameof(conflictResolver));

            var diffCommonBaseToLeft = Diff.AlignElements(commonBase, left, Diff.CalculateSections(commonBase, left, diffOptions, comparer), aligner).ToList();

            Assume.That(diffCommonBaseToLeft != null);
            _DiffCommonBaseToLeft = diffCommonBaseToLeft;

            var diffCommonBaseToRight = Diff.AlignElements(commonBase, right, Diff.CalculateSections(commonBase, right, diffOptions, comparer), aligner).ToList();

            Assume.That(diffCommonBaseToRight != null);
            _DiffCommonBaseToRight = diffCommonBaseToRight;

            var mergeSections = Diff.CalculateSections(diffCommonBaseToLeft, diffCommonBaseToRight, diffOptions, new DiffSectionMergeComparer <T>(comparer)).ToList();

            Assume.That(mergeSections != null);
            _MergeSections = mergeSections;
        }
Ejemplo n.º 3
0
        public static IEnumerable <DiffSection> CalculateSections <T>([NotNull] IList <T> collection1, [NotNull] IList <T> collection2, [CanBeNull] DiffOptions options, [CanBeNull] IEqualityComparer <T> comparer = null)
        {
            if (collection1 == null)
            {
                throw new ArgumentNullException(nameof(collection1));
            }
            if (collection2 == null)
            {
                throw new ArgumentNullException(nameof(collection2));
            }

            comparer = comparer ?? EqualityComparer <T> .Default;
            Assume.That(comparer != null);

            options = options ?? new DiffOptions();

            return(LongestCommonSubsectionDiff.Calculate(collection1, collection2, options, comparer));
        }
Ejemplo n.º 4
0
        public static IEnumerable <T> Perform <T>([NotNull] IList <T> commonBase, [NotNull] IList <T> left, [NotNull] IList <T> right, [CanBeNull] DiffOptions diffOptions, [NotNull] IDiffElementAligner <T> aligner, [NotNull] IMergeConflictResolver <T> conflictResolver, [CanBeNull] IEqualityComparer <T> comparer = null)
        {
            if (commonBase == null)
            {
                throw new ArgumentNullException(nameof(commonBase));
            }
            if (left == null)
            {
                throw new ArgumentNullException(nameof(left));
            }
            if (right == null)
            {
                throw new ArgumentNullException(nameof(right));
            }
            if (aligner == null)
            {
                throw new ArgumentNullException(nameof(aligner));
            }
            if (conflictResolver == null)
            {
                throw new ArgumentNullException(nameof(conflictResolver));
            }

            diffOptions = diffOptions ?? new DiffOptions();
            comparer    = comparer ?? EqualityComparer <T> .Default;
            Assume.That(comparer != null);

            return(new Merge <T>(commonBase, left, right, aligner, conflictResolver, comparer, diffOptions));
        }
 public static IEnumerable <DiffSection> Calculate <T>([NotNull] IList <T> collection1, [NotNull] IList <T> collection2, [NotNull] DiffOptions options, [NotNull] IEqualityComparer <T> comparer)
 {
     return(Calculate(collection1, 0, collection1.Count, collection2, 0, collection2.Count, comparer, new LongestCommonSubsequence <T>(collection1, collection2, comparer), options));
 }
        private static IEnumerable <DiffSection> Calculate <T>([NotNull] IList <T> collection1, int lower1, int upper1, [NotNull] IList <T> collection2, int lower2, int upper2, [NotNull] IEqualityComparer <T> comparer, [NotNull] LongestCommonSubsequence <T> lcs, [NotNull] DiffOptions options)
        {
            // Short-circuit recursive call when nothing left (usually because match was found at the very start or end of a subsection
            if (lower1 == upper1 && lower2 == upper2)
            {
                yield break;
            }

            // Patience modification, let's find matching elements at both ends and remove those from LCS consideration
            int matchEnd = 0;

            if (options.EnablePatienceOptimization)
            {
                int matchStart = MatchStart(collection1, lower1, upper1, collection2, lower2, upper2, comparer);
                if (matchStart > 0)
                {
                    yield return(new DiffSection(isMatch: true, lengthInCollection1: matchStart, lengthInCollection2: matchStart));

                    lower1 += matchStart;
                    lower2 += matchStart;
                }

                matchEnd = MatchEnd(collection1, lower1, upper1, collection2, lower2, upper2, comparer);
                if (matchEnd > 0)
                {
                    upper1 -= matchEnd;
                    upper2 -= matchEnd;
                }
            }

            if (lower1 < upper1 || lower2 < upper2)
            {
                if (lower1 == upper1 || lower2 == upper2)
                {
                    // Degenerate case, only one of the collections still have elements
                    yield return(new DiffSection(isMatch: false, lengthInCollection1: upper1 - lower1, lengthInCollection2: upper2 - lower2));
                }
                else
                {
                    int position1;
                    int position2;
                    int length;

                    if (lcs.Find(lower1, upper1, lower2, upper2, out position1, out position2, out length))
                    {
                        // Recursively apply calculation to portion before common subsequence
                        foreach (var section in Calculate(collection1, lower1, position1, collection2, lower2, position2, comparer, lcs, options))
                        {
                            yield return(section);
                        }

                        // Output match
                        yield return(new DiffSection(isMatch: true, lengthInCollection1: length, lengthInCollection2: length));

                        // Recursively apply calculation to portion after common subsequence
                        foreach (var section in Calculate(collection1, position1 + length, upper1, collection2, position2 + length, upper2, comparer, lcs, options))
                        {
                            yield return(section);
                        }
                    }
                    else
                    {
                        // Unable to find a match, so just return section as unmatched
                        yield return(new DiffSection(isMatch: false, lengthInCollection1: upper1 - lower1, lengthInCollection2: upper2 - lower2));
                    }
                }
            }

            if (matchEnd > 0)
            {
                yield return(new DiffSection(isMatch: true, lengthInCollection1: matchEnd, lengthInCollection2: matchEnd));
            }
        }