Exemple #1
0
 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 override void SetUp()
 {
     base.SetUp();
     scmData = MockRepository.GenerateStub<IScmData>();
     logStub = MockRepository.GenerateStub<ILog>();
     diffStub = MockRepository.GenerateStub<IDiff>();
 }
Exemple #3
0
 public Changestep(TData data, CrudOperation operation, IDiff diff, DateTime timestamp)
 {
     Data      = data;
     Operation = operation;
     Diff      = diff;
     Timestamp = timestamp;
 }
Exemple #4
0
        public TType Apply(TType source, IDiff <TType> patch)
        {
            // ReSharper disable once PossibleInvalidCastExceptionInForeachLoop
            foreach (var item in patch.Cast <IDiffValue>())
            {
                switch (item)
                {
                case IDiffItemReplaced <TType> itemReplaced:
                    return(itemReplaced.NewValue);

                case IDiffItemChanged itemChanged:
                    Type  sourceType = source.GetType();
                    IDiff diff       = itemChanged.ValueDiff;
                    IApplyPatchAlgorithm algorithm;

                    if (!this.aTypes.TryGetValue(sourceType, out algorithm))
                    {
                        algorithm = this.aMergerImplementation.Partial.Algorithms.GetApplyPatchAlgorithm(sourceType, this.aRules);
                    }

                    return((TType)algorithm.Apply(source, diff));

                default:
                    throw new InvalidOperationException();
                }
            }

            return(source);
        }
Exemple #5
0
 private static IDiff[] makediffs (IList original, IList [] changed, IComparer comparer, IHashCodeProvider 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;
 }
        /// <summary>
        /// Verifies that the <paramref name="actualChange"/> diff is a change to the value of the specific attribute (<paramref name="expectedAttrName"/>).
        /// </summary>
        /// <param name="actualChange">The actual change that has happened.</param>
        /// <param name="expectedAttrName">The expected name of the changed attribute.</param>
        /// <param name="expectedAttrValue">The expected value of the changed attribute.</param>
        /// <param name="userMessage">A custom user message to show when the verification fails.</param>
        public static void ShouldBeAttributeChange(this IDiff actualChange, string expectedAttrName, string expectedAttrValue, string?userMessage = null)
        {
            if (actualChange is null)
            {
                throw new ArgumentNullException(nameof(actualChange));
            }
            if (expectedAttrName is null)
            {
                throw new ArgumentNullException(nameof(expectedAttrName));
            }
            if (expectedAttrValue is null)
            {
                throw new ArgumentNullException(nameof(expectedAttrValue));
            }

            var actual = Assert.IsType <AttrDiff>(actualChange);

            if (!expectedAttrName.Equals(actual.Test.Attribute.Name, StringComparison.Ordinal))
            {
                throw new AssertActualExpectedException(
                          expectedAttrName, actual.Test.Attribute.Name,
                          userMessage ?? "The name of the changed attribute does not match the expected name.",
                          "Expected attribute name",
                          "Actual attribute name");
            }
            if (!expectedAttrValue.Equals(actual.Test.Attribute.Value, StringComparison.Ordinal))
            {
                throw new AssertActualExpectedException(
                          expectedAttrValue, actual.Test.Attribute.Value,
                          userMessage ?? "The value of the changed attribute does not match the expected value.",
                          "Expected attribute value",
                          "Actual attribute value");
            }
        }
Exemple #7
0
        public void Process(IDiff diff, IKoreFolderInfo source, IKoreFolderInfo destination)
        {
            IsNotNull(diff);

            if (diff.Relation == DiffRelation.Identical)
            {
                return;
            }

            if (diff.Relation == DiffRelation.DestinationOrphan)
            {
                diff.Destination.Delete();
                return;
            }

            IsNotNull(source);
            IsNotNull(destination);

            var sourceFileInfo      = diff.Source;
            var destinationFileInfo = diff.Destination;

            if (diff.Relation == DiffRelation.SourceNew)
            {
                var sourceInnerFullPath = BuildRelativePath(diff.Source, source);
                destinationFileInfo = new KoreFileInfo(Path.Combine(destination.FullName, sourceInnerFullPath));
            }
            else if (diff.Relation == DiffRelation.SourceOlder)
            {
                sourceFileInfo      = destinationFileInfo;
                destinationFileInfo = diff.Source;
            }

            _fileCopier.Copy(sourceFileInfo, destinationFileInfo);
        }
        private TType ApplyInternal(IEnumerable <TItemType> source, IDiff <TType> patch)
        {
            HashSet <TItemType> ret = new HashSet <TItemType>(source, this.aItemComparer);

            foreach (var item in patch.Cast <IDiffUnorderedCollectionItem>())
            {
                switch (item)
                {
                case IDiffItemAdded <TItemType> itemAdded:
                    ret.Add(itemAdded.NewValue);
                    break;

                case IDiffItemRemoved <TItemType> itemRemoved:
                    ret.Remove(itemRemoved.OldValue);
                    break;

                case IDiffItemReplaced <TItemType> itemReplaced:
                    ret.Remove(itemReplaced.OldValue);
                    ret.Add(itemReplaced.NewValue);
                    break;

                default:
                    throw new InvalidOperationException();
                }
            }

            return(this.aConvertor(ret));
        }
Exemple #9
0
        public IDiff <TType> MergeDiffs(IDiff <TType> left, IDiff <TType> right, IConflictContainer conflicts)
        {
            if (this.aMergeItemsDiffs == null)
            {
                this.aMergeItemsDiffs = this.aMergerImplementation.Partial.Algorithms.GetMergeDiffsAlgorithm <TItemType>();
            }

            List <IDiffItem> ret = new List <IDiffItem>();

            foreach (var(leftItems, rightItems) in new MergeJoin(new Chunker(left.Cast <IDiffOrderedCollectionItem>()), new Chunker(right.Cast <IDiffOrderedCollectionItem>())))
            {
                if (leftItems == null)
                {
                    ret.AddRange(rightItems);
                }
                else if (rightItems == null)
                {
                    ret.AddRange(leftItems);
                }
                else
                {
                    this.ProcessConflicts(ret, leftItems, rightItems, conflicts);
                }
            }

            return(new Diff <TType>(ret));
        }
 public override void SetUp()
 {
     base.SetUp();
     scmData  = MockRepository.GenerateStub <IScmData>();
     logStub  = MockRepository.GenerateStub <ILog>();
     diffStub = MockRepository.GenerateStub <IDiff>();
 }
        /// <summary>
        /// Verifies that the <paramref name="actualChange"/> diff is a change to the value of the specific attribute (<paramref name="expectedAttrName"/>).
        /// </summary>
        /// <param name="actualChange">The actual change that has happened.</param>
        /// <param name="expectedAttrName">The expected name of the changed attribute.</param>
        /// <param name="expectedAttrValue">The expected value of the changed attribute.</param>
        /// <param name="userMessage">A custom user message to show when the verification fails.</param>
        public static void ShouldBeAttributeChange(this IDiff actualChange, string expectedAttrName, string expectedAttrValue, string?userMessage = null)
        {
            if (actualChange is null)
            {
                throw new ArgumentNullException(nameof(actualChange));
            }
            if (expectedAttrName is null)
            {
                throw new ArgumentNullException(nameof(expectedAttrName));
            }
            if (expectedAttrValue is null)
            {
                throw new ArgumentNullException(nameof(expectedAttrValue));
            }

            var actual = actualChange as AttrDiff ?? throw new DiffChangeAssertException(actualChange.Result, DiffResult.Different, "The change was not a attribute change.");

            if (!expectedAttrName.Equals(actual.Test.Attribute.Name, StringComparison.Ordinal))
            {
                throw new ActualExpectedAssertException(
                          actual.Test.Attribute.Name, expectedAttrName,
                          "Actual attribute name",
                          "Expected attribute name",
                          userMessage ?? "The name of the changed attribute does not match the expected name.");
            }
            if (!expectedAttrValue.Equals(actual.Test.Attribute.Value, StringComparison.Ordinal))
            {
                throw new ActualExpectedAssertException(
                          actual.Test.Attribute.Value,
                          expectedAttrValue,
                          "Actual attribute value",
                          "Expected attribute value",
                          userMessage ?? "The value of the changed attribute does not match the expected value.");
            }
        }
Exemple #12
0
        public DynamicDiff(IDiff <TType> original, Dictionary <IDiffItemConflicted, ResolveAction> resolveActions)
        {
            this.aOriginal       = original;
            this.aResolveActions = resolveActions;

            this.aDynamicDiffItemChangedFactory = new DynamicDiffItemChangedFactory(this.aResolveActions, false);
            this.aFinishDiffItemChangedFactory  = new DynamicDiffItemChangedFactory(this.aResolveActions, true);
        }
        public IConflictResolver <TType> GetConflictResolver <TType>(IDiff <TType> conflicted)
        {
            var conflicts = new ConflictContainer();

            conflicts.RegisterAll(conflicted);

            return(new ConflictResolver <TType>(conflicted, conflicts));
        }
Exemple #14
0
 public ConflictResolver(IDiff <TType> original, ConflictContainer conflicts)
 {
     this.aOriginal       = original;
     this.aConflicts      = conflicts.GetConflicts().ToList();
     this.aResolveActions = new Dictionary <IDiffItemConflicted, ResolveAction>(this.aConflicts.Count, aConflictComparer);
     this.aResolved       = new DynamicDiff <TType>(this.aOriginal, this.aResolveActions);
     this.aFinished       = false;
 }
 /// <summary>
 /// Verifies that a diff is a change to a text node.
 /// </summary>
 /// <param name="actualChange">The diff to verify.</param>
 /// <param name="expectedChange">The rendered fragment containing the expected text change.</param>
 /// <param name="userMessage">A custom error message to show if the verification fails.</param>
 public static void ShouldBeTextChange(this IDiff actualChange, IRenderedFragment expectedChange, string?userMessage = null)
 {
     if (expectedChange is null)
     {
         throw new ArgumentNullException(nameof(expectedChange));
     }
     ShouldBeTextChange(actualChange, expectedChange.Nodes, userMessage);
 }
        public void Test003()
        {
            var input = new IDiff[] { Mock.Of <IDiff>() };

            var output = input.ShouldHaveSingleChange();

            output.ShouldBe(input[0]);
        }
Exemple #17
0
 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);
 }
Exemple #18
0
 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 TType Apply(TType source, IDiff <TType> patch)
        {
            if (this.aItemComparer == null)
            {
                this.aItemComparer = EqualityComparer <TItemType> .Default;
                this.aConvertor    = this.CompileConvertor();
            }

            return(this.ApplyInternal((IEnumerable <TItemType>)source, patch));
        }
Exemple #20
0
        public TType Apply(TType source, IDiff <TType> patch)
        {
            if (this.aConvertor == null)
            {
                this.aConvertor     = this.CompileConvertor();
                this.aApplyItemDiff = this.aMergerImplementation.Partial.Algorithms.GetApplyPatchAlgorithm <TItemType>();
            }

            return(this.ApplyInternal((IEnumerable <KeyValuePair <TKeyType, TItemType> >)source, patch));
        }
Exemple #21
0
        private TType ApplyInternal(IEnumerable <TItemType> source, IDiff <TType> patch)
        {
            List <TItemType> ret = new List <TItemType>();

            using (IEnumerator <TItemType> enumerator = source.GetEnumerator())
            {
                bool lastMoveNext = enumerator.MoveNext();
                int  currentIndex = 0;

                foreach (var item in patch.Cast <IDiffOrderedCollectionItem>())
                {
                    while (currentIndex < item.ItemIndex)
                    {
                        ret.Add(enumerator.Current);
                        lastMoveNext = enumerator.MoveNext();
                        currentIndex++;
                    }

                    switch (item)
                    {
                    case IDiffItemAdded <TItemType> itemAdded:
                        ret.Add(itemAdded.NewValue);
                        break;

                    case IDiffItemChanged <TItemType> itemChanged:
                        ret.Add(this.aApplyItemDiff.Apply(enumerator.Current, itemChanged.ValueDiff));
                        lastMoveNext = enumerator.MoveNext();
                        currentIndex++;
                        break;

                    case IDiffItemRemoved <TItemType> _:
                        lastMoveNext = enumerator.MoveNext();
                        currentIndex++;
                        break;

                    case IDiffItemReplaced <TItemType> itemReplaced:
                        lastMoveNext = enumerator.MoveNext();
                        ret.Add(itemReplaced.NewValue);
                        currentIndex++;
                        break;

                    default:
                        throw new InvalidOperationException();
                    }
                }

                while (lastMoveNext)
                {
                    ret.Add(enumerator.Current);
                    lastMoveNext = enumerator.MoveNext();
                }
            }

            return(this.aConvertor(ret));
        }
Exemple #22
0
        public TType Apply(TType source, IDiff <TType> patch)
        {
            if (this.aCompiled == null)
            {
                var compiled = this.Compile();

                this.aCompiled = compiled.Compile();
            }

            return(this.aCompiled(source, patch));
        }
Exemple #23
0
        public static byte[] Write(IDiff diff)
        {
            var stream = new MemoryStream();
            var writer = new BinaryWriter(stream);

            WriteHeader(diff, writer);
            Write(diff, writer, diff.HasZ());
            writer.Flush();
            stream.Flush();
            return(stream.ToArray());
        }
        public TType Apply(TType source, IDiff <TType> patch)
        {
            if (this.aIdAccessor == null)
            {
                this.aIdAccessor    = IdHelpers.CreateIdAccessor <TItemType, TIdType>(this.aIdProperty);
                this.aApplyItemDiff = this.aMergerImplementation.Partial.Algorithms.GetApplyPatchAlgorithm <TItemType>();
                this.aConvertor     = this.CompileConvertor();
            }

            return(this.ApplyInternal((IEnumerable <TItemType>)source, patch));
        }
        public IDiff <TType> MergeDiffs(IDiff <TType> left, IDiff <TType> right, IConflictContainer conflicts)
        {
            if (this.aIdAccessor == null)
            {
                if (this.aIdProperty == null)
                {
                    ParameterExpression obj = Expression.Parameter(typeof(TItemType), "obj");

                    Expression <Func <TItemType, TIdType> > identityFunction = Expression.Lambda <Func <TItemType, TIdType> >(
                        obj, obj
                        );

                    this.aIdAccessor = identityFunction.Compile();
                }
                else
                {
                    this.aIdAccessor = IdHelpers.CreateIdAccessor <TItemType, TIdType>(this.aIdProperty);
                }

                this.aMergeItemsDiffs = this.aMergerImplementation.Partial.Algorithms.GetMergeDiffsAlgorithm <TItemType>();
            }

            Dictionary <TIdType, IDiffUnorderedCollectionItem> rightIndex = new Dictionary <TIdType, IDiffUnorderedCollectionItem>(right.Count);

            foreach (IDiffItem item in right)
            {
                rightIndex[this.GetID(item)] = (IDiffUnorderedCollectionItem)item;
            }

            List <IDiffItem> ret = new List <IDiffItem>(left.Count + right.Count);

            foreach (var leftItem in left.Cast <IDiffUnorderedCollectionItem>())
            {
                IDiffUnorderedCollectionItem rightItem;

                TIdType id = this.GetID(leftItem);

                if (rightIndex.TryGetValue(id, out rightItem))
                {
                    rightIndex.Remove(id);

                    this.ProcessConflict(id, leftItem, rightItem, ret, conflicts);
                }
                else
                {
                    ret.Add(leftItem);
                }
            }

            ret.AddRange(rightIndex.Values);

            return(new Diff <TType>(ret));
        }
        public IDiff <TType> MergeDiffs(IDiff <TType> left, IDiff <TType> right, IConflictContainer conflicts)
        {
            if (this.aCompiled == null)
            {
                var compiled = this.Compile();

                this.aCompiled = compiled.Compile();
            }

            var ret = this.aCompiled(left, right, conflicts);

            return(new Diff <TType>(ret));
        }
Exemple #27
0
        public void RegisterAll(IDiff diff)
        {
            foreach (IDiffItem item in diff)
            {
                switch (item)
                {
                case IDiffItemConflicted itemConflicted:
                    this.RegisterConflict(itemConflicted);
                    break;

                case IDiffItemChanged itemChanged:
                    this.RegisterAll(itemChanged.ValueDiff);
                    break;
                }
            }
        }
Exemple #28
0
        void RunOnBuilders(IActivityMonitor m, IDiff diff)
        {
            bool accepted = false;

            foreach (var builder in _diffRootResultBuilders)
            {
                if (diff.SendToBuilder(m, builder))
                {
                    accepted = true;
                }
            }
            if (!accepted)
            {
                diff.SendToBuilder(m, _others);
            }
        }
        private IDiff <TType> ComputeInternal(IEnumerable <TItemType> @base, IEnumerable <TItemType> changed)
        {
            Dictionary <TIdType, TItemType> changedSet = changed.ToDictionary(this.aIdAccessor);

            List <IDiffItem> ret = new List <IDiffItem>(20);           // 20 seems to be a good value :)

            foreach (TItemType baseItem in @base)
            {
                TIdType id = this.aIdAccessor(baseItem);

                TItemType changedItem;

                if (changedSet.TryGetValue(id, out changedItem))
                {
                    changedSet.Remove(id);

                    if (this.aItemDiff.IsDirect)
                    {
                        if (!this.aItemComparer.Equals(baseItem, changedItem))
                        {
                            ret.Add(new DiffUnorderedCollectionReplaced <TItemType>(baseItem, changedItem));
                        }
                    }
                    else
                    {
                        IDiff <TItemType> itemDiff = this.aItemDiff.Compute(baseItem, changedItem);

                        if (itemDiff.HasChanges)
                        {
                            ret.Add(new DiffUnorderedCollectionChanged <TIdType, TItemType>(id, itemDiff));
                        }
                    }
                }
                else
                {
                    ret.Add(new DiffUnorderedCollectionRemoved <TItemType>(baseItem));
                }
            }

            foreach (KeyValuePair <TIdType, TItemType> item in changedSet)
            {
                ret.Add(new DiffUnorderedCollectionAdded <TItemType>(item.Value));
            }

            return(new Diff <TType>(ret));
        }
        /// <summary>
        /// Verifies that a diff is a change to a text node.
        /// </summary>
        /// <param name="actualChange">The diff to verify.</param>
        /// <param name="expectedChange">The expected text change.</param>
        /// <param name="userMessage">A custom error message to show if the verification fails.</param>
        public static void ShouldBeTextChange(this IDiff actualChange, string expectedChange, string?userMessage = null)
        {
            if (actualChange is null)
            {
                throw new ArgumentNullException(nameof(actualChange));
            }
            if (expectedChange is null)
            {
                throw new ArgumentNullException(nameof(expectedChange));
            }

            var actual   = Assert.IsType <NodeDiff>(actualChange);
            var parser   = actual.Control.Node.Owner.Context.GetService <TestHtmlParser>();
            var expected = parser.Parse(expectedChange);

            ShouldBeTextChange(actualChange, expected, userMessage);
        }
Exemple #31
0
            static bool ExcludeDiff(IDiff diff)
            {
                // Handle aria-describedby being out of order
                // e.g. 'bar foo' instead of 'foo bar'
                if (diff is AttrDiff attrDiff && attrDiff.Test.Attribute.Name == "aria-describedby")
                {
                    var controlValueParts = attrDiff.Control.Attribute.Value
                                            .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                    var testValueParts = attrDiff.Test.Attribute.Value
                                         .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                    return(controlValueParts.OrderBy(v => v, StringComparer.OrdinalIgnoreCase)
                           .SequenceEqual(testValueParts.OrderBy(v => v, StringComparer.OrdinalIgnoreCase)));
                }

                return(false);
            }
        /// <summary>
        /// Verifies that the <paramref name="actualChange"/> <see cref="IDiff"/> is an removal,
        /// i.e. that one or more nodes have been removed, and verifies that the removed nodes are equal
        /// to the markup specified in the <paramref name="expectedChange"/> input.
        /// </summary>
        /// <param name="actualChange">The change to verify</param>
        /// <param name="expectedChange">The expected removal to verify against</param>
        /// <param name="userMessage">A custom user message to display in case the verification fails.</param>
        public static void ShouldBeRemoval(this IDiff actualChange, string expectedChange, string?userMessage = null)
        {
            if (actualChange is null)
            {
                throw new ArgumentNullException(nameof(actualChange));
            }
            if (expectedChange is null)
            {
                throw new ArgumentNullException(nameof(expectedChange));
            }

            var       actual = Assert.IsType <MissingNodeDiff>(actualChange);
            INodeList expected;

            if (actual.Control.Node.GetHtmlParser() is { } parser)
            {
                expected = parser.Parse(expectedChange);
            }
        /// <summary>
        /// Verifies that a diff is a change to a text node.
        /// </summary>
        /// <param name="actualChange">The diff to verify.</param>
        /// <param name="expectedChange">The expected text change.</param>
        /// <param name="userMessage">A custom error message to show if the verification fails.</param>
        public static void ShouldBeTextChange(this IDiff actualChange, string expectedChange, string?userMessage = null)
        {
            if (actualChange is null)
            {
                throw new ArgumentNullException(nameof(actualChange));
            }
            if (expectedChange is null)
            {
                throw new ArgumentNullException(nameof(expectedChange));
            }

            var actual = actualChange as NodeDiff ?? throw new DiffChangeAssertException(actualChange.Result, DiffResult.Different, "The change was not a text change.");

            var parser   = actual.Control.Node.Owner.Context.GetService <HtmlParser>();
            var expected = parser.Parse(expectedChange);

            ShouldBeTextChange(actualChange, expected, userMessage);
        }
		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);
				}
				
			}
		}