public void AlignElements_NullAligner_ThrowsArgumentNullException() { IList <int> collection1 = new int[0]; IList <int> collection2 = new int[0]; IEnumerable <DiffSection> diffSections = new DiffSection[0]; IDiffElementAligner <int> aligner = null; Assert.Throws <ArgumentNullException>(() => Diff.AlignElements(collection1, collection2, diffSections, aligner)); }
/// <summary> /// Construct a new <see cref="StringSimilarity"/>. /// </summary> /// <param name="modificationThreshold"> /// A threshold value used to determine if aligned elements are considered replacements or modifications. If /// two items are more similar than the threshold specifies (similarity > threshold), then it results in /// a <see cref="DiffOperation.Modify"/>, otherwise it results in a <see cref="DiffOperation.Replace"/>. /// </param> public StringSimilarityDiffElementAligner(double modificationThreshold = 0.3333) { _Aligner = new ElementSimilarityDiffElementAligner <string>(StringSimilarity, modificationThreshold); }
/// <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(); } } }
/// <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="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] IEqualityComparer <T> comparer = null, [CanBeNull] IDiffElementAligner <T> aligner = null) { MutateToBeLike(target, source, new DiffOptions(), comparer, aligner); }
public static IEnumerable <DiffElement <T> > AlignElements <T>([NotNull] IList <T> collection1, [NotNull] IList <T> collection2, [NotNull] IEnumerable <DiffSection> diffSections, [NotNull] IDiffElementAligner <T> aligner) { if (collection1 == null) { throw new ArgumentNullException(nameof(collection1)); } if (collection2 == null) { throw new ArgumentNullException(nameof(collection2)); } if (diffSections == null) { throw new ArgumentNullException(nameof(diffSections)); } if (aligner == null) { throw new ArgumentNullException(nameof(aligner)); } return(AlignElementsImplementation(collection1, collection2, diffSections, aligner)); }
private static IEnumerable <DiffElement <T> > AlignElementsImplementation <T>([NotNull] IList <T> collection1, [NotNull] IList <T> collection2, [NotNull] IEnumerable <DiffSection> diffSections, [NotNull] IDiffElementAligner <T> aligner) { int start1 = 0; int start2 = 0; foreach (var section in diffSections) { if (section.IsMatch) { for (int index = 0; index < section.LengthInCollection1; index++) { yield return(new DiffElement <T>(collection1[start1], collection2[start2], DiffOperation.Match)); start1++; start2++; } } else { foreach (var element in aligner.Align(collection1, start1, section.LengthInCollection1, collection2, start2, section.LengthInCollection2)) { yield return(element); } start1 += section.LengthInCollection1; start2 += section.LengthInCollection2; } } }
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; }
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 <T> Perform <T>([NotNull] IList <T> commonBase, [NotNull] IList <T> left, [NotNull] IList <T> right, [NotNull] IDiffElementAligner <T> aligner, [NotNull] IMergeConflictResolver <T> conflictResolver, [CanBeNull] IEqualityComparer <T> comparer = null) { return(Perform(commonBase, left, right, new DiffOptions(), aligner, conflictResolver, comparer)); }