/* * Merges, a supporting class for Diffs */ using System; using System.Collections; using System.Text; namespace MonoDevelop.Components.Diff { public class Merge : IEnumerable { IDiff[] diffs; ArrayList hunks = new ArrayList(); public Merge(IList original, IList[] changed, IComparer comparer, IEqualityComparer hashcoder) : this(makediffs(original, changed, comparer, hashcoder)) { } public Merge(string original, string[] changed, IComparer comparer) : this(makediffs(original, changed, comparer)) { } private static IDiff[] makediffs(IList original, IList[] changed, IComparer comparer, IEqualityComparer hashcoder) { IDiff[] diffs = new IDiff[changed.Length]; for (int i = 0; i < changed.Length; i++) diffs[i] = new Diff(original, changed[i], comparer, hashcoder); return diffs; } private static IDiff[] makediffs(string original, string[] changed, IComparer comparer) { IDiff[] diffs = new IDiff[changed.Length]; for (int i = 0; i < changed.Length; i++) diffs[i] = new TextDiff(original, changed[i], comparer); return diffs; } public Merge(IDiff[] diffs) { this.diffs = diffs; // initialize data structures IEnumerator[] enumerators = new IEnumerator[diffs.Length]; ArrayList[] hunks = new ArrayList[diffs.Length]; for (int i = 0; i < hunks.Length; i++) { enumerators[i] = ((IEnumerable)diffs[i]).GetEnumerator(); hunks[i] = new ArrayList(); } int startline = 0; while (true) { int endline = -1; bool hasmore = false; // Get the next hunk for each diff, and find the longest // hunk for which there are changes. for (int i = 0; i < hunks.Length; i++) { if (hunks[i].Count > 0) continue; if (!enumerators[i].MoveNext()) return; hasmore = true; Diff.Hunk hunk = (Diff.Hunk)enumerators[i].Current; hunks[i].Add(hunk); if (!hunk.Same && hunk.Left.End > endline) endline = hunk.Left.End; } if (!hasmore) return; if (endline == -1) { // All of the hunks represented no change. Find the shortest hunk, // create a hunk from the current start line to the end of the // shortest hunk, and retain all of the hunks that overlap into that // hunk's next region. (Clear the rest.) int start = int.MaxValue; for (int i = 0; i < hunks.Length; i++) { Diff.Hunk h = (Diff.Hunk)hunks[i][0]; if (h.Left.End < start) start = h.Left.End; } // Crop all of the hunks to the shortest region. Diff.Hunk[][] h2 = new Diff.Hunk[hunks.Length][]; for (int i = 0; i < hunks.Length; i++) { h2[i] = new Diff.Hunk[1]; h2[i][0] = (Diff.Hunk)hunks[i][0]; h2[i][0] = h2[i][0].Crop(startline - h2[i][0].Left.Start, h2[i][0].Left.End - start); } this.hunks.Add( new Hunk(this, h2, startline, start - startline + 1, true) ); for (int i = 0; i < hunks.Length; i++) { Diff.Hunk h = (Diff.Hunk)hunks[i][0]; if (h.Left.End == start) hunks[i].Clear(); } startline = start+1; continue; } // For each diff, add in all of the non-same hunks that fall // at least partially within the largest hunk region. If // a hunk crosses the edge, push the edge further and then // add more hunks again. bool moreToAdd = true; while (moreToAdd) { moreToAdd = false; for (int i = 0; i < hunks.Length; i++) { Diff.Hunk last = (Diff.Hunk)hunks[i][hunks[i].Count-1]; while (last.Left.End < endline) { if (!enumerators[i].MoveNext()) continue; last = (Diff.Hunk)enumerators[i].Current; hunks[i].Add(last); if (last.Same) continue; if (last.Left.End > endline) { endline = last.Left.End; moreToAdd = true; } } } } Diff.Hunk[][] hunks2 = new Diff.Hunk[hunks.Length][]; for (int i = 0; i < hunks.Length; i++) { // any same hunks that overlap the start or end need to be replaced ArrayList hunks3 = new ArrayList(); foreach (Diff.Hunk h in hunks[i]) { Diff.Hunk h2 = h; int shiftstart = 0, shiftend = 0; if (h2.Same && h2.Left.Start < startline) shiftstart = startline - h2.Left.Start; if (h2.Same && h2.Left.End > endline) shiftend = h2.Left.End - endline; if (shiftstart != 0 || shiftend != 0) h2 = h2.Crop(shiftstart, shiftend); hunks3.Add(h2); } hunks2[i] = (Diff.Hunk[])hunks3.ToArray(typeof(Diff.Hunk)); } this.hunks.Add( new Hunk(this, hunks2, startline, endline - startline + 1, false) ); // In each hunk list, retain only the last hunk if it // overlaps into the next region. startline = endline+1; for (int i = 0; i < hunks.Length; i++) { if (hunks[i].Count == 0) continue; Diff.Hunk h = (Diff.Hunk)hunks[i][hunks[i].Count-1]; hunks[i].Clear(); if (h.Left.End >= startline) hunks[i].Add(h); } } } IEnumerator IEnumerable.GetEnumerator() { return hunks.GetEnumerator(); } public static IList MergeLists(IList original, IList[] changed, IComparer comparer, IEqualityComparer hashcoder) { Merge m = new Merge(original, changed, comparer, hashcoder); ArrayList ret = new ArrayList(); ArrayList newlines = new ArrayList(); foreach (Hunk h in m) { newlines.Clear(); for (int i = 0; i < changed.Length; i++) if (!h.IsSame(i)) newlines.Add(h.Changes(i)); // If there were no differences in this region, take the original. if (newlines.Count == 0) ret.AddRange(h.Original()); // If one list has changes, take them else if (newlines.Count == 1) ret.AddRange((Range)newlines[0]); // Indicate conflict else ret.Add(new Conflict((Range[])newlines.ToArray(typeof(Range)))); } return ret; } public class Conflict : IEnumerable { Range[] ranges; internal Conflict(Range[] ranges) { this.ranges = ranges; } public Range[] Ranges { get { return ranges; } } IEnumerator IEnumerable.GetEnumerator() { return ranges.GetEnumerator(); } public override string ToString() { StringBuilder b = new StringBuilder(); b.Append("<<<<<<<<<<"); b.Append(Environment.NewLine); for (int i = 0; i < ranges.Length; i++) { if (i > 0) { b.Append("----------"); b.Append(Environment.NewLine); } foreach (object item in ranges[i]) { b.Append(item); b.Append(Environment.NewLine); } } b.Append(">>>>>>>>>>"); b.Append(Environment.NewLine); return b.ToString(); } } public class Hunk : MonoDevelop.Components.Diff.Hunk { Merge merge; Diff.Hunk[][] hunks; int start, count; bool same, conflict; internal Hunk(Merge merge, Diff.Hunk[][] hunks, int start, int count, bool same) { this.merge = merge; this.hunks = hunks; this.start = start; this.count = count; this.same = same; int ct = 0; foreach (Diff.Hunk[] hh in hunks) { foreach (Diff.Hunk h in hh) { if (!h.Same) { ct++; break; } } } conflict = (ct > 1); } public override int ChangedLists { get { return merge.diffs.Length; } } // Returns the set of changes within this hunk's range for the // diff of the given index. public Diff.Hunk[] ChangesHunks(int index) { return hunks[index]; } public int ChangedIndex() { if (Conflict) throw new InvalidOperationException("ChangedIndex cannot be called if there is a conflict."); for (int i = 0; i < hunks.Length; i++) { foreach (Diff.Hunk h in hunks[i]) if (!h.Same) return i; } return -1; } // Returns the range of elements corresponding to this hunk's range, in the original. public override Range Original() { return new Range(merge.diffs[0].Left, start, count); } // Returns the range of elements corresponding to this hunk's range, in the diff of the given index. public override Range Changes(int index) { return new Range(merge.diffs[index].Right, hunks[index][0].Right.Start, hunks[index][hunks[index].Length-1].Right.End - hunks[index][0].Right.Start + 1); } public override bool Same { get { return same; } } public override bool Conflict { get { return conflict; } } public override bool IsSame(int index) { foreach (Diff.Hunk h in hunks[index]) if (!h.Same) return false; return true; } public override string ToString() { StringBuilder b = new StringBuilder(); if (Same) { foreach (object item in Original()) { b.Append(' '); b.Append(item); b.Append(Environment.NewLine); } return b.ToString(); } b.Append("=========="); b.Append(Environment.NewLine); for (int i = 0; i < hunks.Length; i++) { if (i > 0) { b.Append("----------"); b.Append(Environment.NewLine); } foreach (Diff.Hunk h in hunks[i]) b.Append(h); } b.Append("=========="); b.Append(Environment.NewLine); return b.ToString(); } } } }