From 307ba53eb65f616252567bfc6b8c52703a192be1 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 6 Sep 2010 13:33:31 -0700 Subject: [PATCH] Define DiffAlgorithm as an abstract function This makes it easier to parametrize DiffFormatter with a different implementation, as we later plan to add PatienceDiff to JGit. Change-Id: Id35ef478d5fa20fe10a1ba297f9436fd7adde9ce Signed-off-by: Shawn O. Pearce --- .../eclipse/jgit/iplog/IpLogGenerator.java | 4 +- .../jgit/diff/MyersDiffPerformanceTest.java | 6 +- .../org/eclipse/jgit/diff/MyersDiffTest.java | 4 +- .../org/eclipse/jgit/diff/DiffAlgorithm.java | 79 +++++++++++++++++++ .../org/eclipse/jgit/diff/DiffFormatter.java | 15 +++- .../src/org/eclipse/jgit/diff/MyersDiff.java | 19 ++--- .../eclipse/jgit/merge/MergeAlgorithm.java | 4 +- 7 files changed, 112 insertions(+), 19 deletions(-) create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java diff --git a/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogGenerator.java b/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogGenerator.java index 5013eb6b8..66345bb7d 100644 --- a/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogGenerator.java +++ b/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogGenerator.java @@ -384,9 +384,9 @@ public class IpLogGenerator { else oldImage = new byte[0]; - EditList edits = new MyersDiff( + EditList edits = MyersDiff.INSTANCE.diff( RawTextComparator.DEFAULT, new RawText(oldImage), - new RawText(openBlob(1))).getEdits(); + new RawText(openBlob(1))); for (Edit e : edits) addedLines += e.getEndB() - e.getBeginB(); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/MyersDiffPerformanceTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/MyersDiffPerformanceTest.java index 38fac3cc9..a3f9c22e3 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/MyersDiffPerformanceTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/MyersDiffPerformanceTest.java @@ -164,14 +164,14 @@ public class MyersDiffPerformanceTest extends TestCase { CharArray ac = new CharArray(a); CharArray bc = new CharArray(b); CharCmp cmp = new CharCmp(); - MyersDiff myersDiff = null; + int D = 0; int cpuTimeChanges = 0; long lastReadout = 0; long interimTime = 0; int repetitions = 0; stopwatch.start(); while (cpuTimeChanges < minCPUTimerTicks && interimTime < longTaskBoundary) { - myersDiff = new MyersDiff(cmp, ac, bc); + D = MyersDiff.INSTANCE.diff(cmp, ac, bc).size(); repetitions++; interimTime = stopwatch.readout(); if (interimTime != lastReadout) { @@ -181,7 +181,7 @@ public class MyersDiffPerformanceTest extends TestCase { } ret.runningTime = stopwatch.stop() / repetitions; ret.N = ac.size() + bc.size(); - ret.D = myersDiff.getEdits().size(); + ret.D = D; return ret; } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/MyersDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/MyersDiffTest.java index 60c1b4728..2410e8eab 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/MyersDiffTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/MyersDiffTest.java @@ -63,9 +63,9 @@ public class MyersDiffTest extends TestCase { } public void assertDiff(String a, String b, String edits) { - MyersDiff diff = new MyersDiff(new CharCmp(), + EditList editList = MyersDiff.INSTANCE.diff(new CharCmp(), toCharArray(a), toCharArray(b)); - assertEquals(edits, toString(diff.getEdits())); + assertEquals(edits, toString(editList)); } private static String toString(EditList list) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java new file mode 100644 index 000000000..94cb0b60f --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.diff; + +/** + * Compares two {@link Sequence}s to create an {@link EditList} of changes. + * + * An algorithm's {@code diff} method must be callable from concurrent threads + * without data collisions. This permits some algorithms to use a singleton + * pattern, with concurrent invocations using the same singleton. Other + * algorithms may support parameterization, in which case the caller can create + * a unique instance per thread. + */ +public interface DiffAlgorithm { + /** + * Compare two sequences and identify a list of edits between them. + * + * @param + * type of sequence being compared. + * @param + * type of comparator to evaluate the sequence elements. + * @param cmp + * the comparator supplying the element equivalence function. + * @param a + * the first (also known as old or pre-image) sequence. Edits + * returned by this algorithm will reference indexes using the + * 'A' side: {@link Edit#getBeginA()}, {@link Edit#getEndA()}. + * @param b + * the second (also known as new or post-image) sequence. Edits + * returned by this algorithm will reference indexes using the + * 'B' side: {@link Edit#getBeginB()}, {@link Edit#getEndB()}. + * @return a modifiable edit list comparing the two sequences. If empty, the + * sequences are identical according to {@code cmp}'s rules. The + * result list is never null. + */ + public > EditList diff( + C cmp, S a, S b); +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java index e36aaf17f..ec1ced62a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java @@ -118,6 +118,8 @@ public class DiffFormatter { private int abbreviationLength = 7; + private DiffAlgorithm diffAlgorithm = MyersDiff.INSTANCE; + private RawTextComparator comparator = RawTextComparator.DEFAULT; private int binaryFileThreshold = DEFAULT_BINARY_FILE_THRESHOLD; @@ -206,6 +208,17 @@ public class DiffFormatter { abbreviationLength = count; } + /** + * Set the algorithm that constructs difference output. + * + * @param alg + * the algorithm to produce text file differences. + * @see MyersDiff#INSTANCE + */ + public void setDiffAlgorithm(DiffAlgorithm alg) { + diffAlgorithm = alg; + } + /** * Set the line equivalence function for text file differences. * @@ -893,7 +906,7 @@ public class DiffFormatter { } private EditList diff(RawText a, RawText b) { - return new MyersDiff(comparator, a, b).getEdits(); + return diffAlgorithm.diff(comparator, a, b); } private void assertHaveRepository() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java index 270ed9274..45abb58cd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java @@ -106,13 +106,21 @@ import org.eclipse.jgit.util.LongList; * type of sequence. */ public class MyersDiff { + /** Singleton instance of MyersDiff. */ + public static final DiffAlgorithm INSTANCE = new DiffAlgorithm() { + public > EditList diff( + C cmp, S a, S b) { + return new MyersDiff(cmp, a, b).getEdits(); + } + }; + /** * The list of edits found during the last call to {@link #calculateEdits()} */ protected EditList edits; /** Comparison function for sequences. */ - protected SequenceComparator cmp; + protected SequenceComparator cmp; /** * The first text to be compared. Referred to as "Text A" in the comments @@ -124,14 +132,7 @@ public class MyersDiff { */ protected S b; - /** - * The only constructor - * - * @param cmp comparison method for this execution. - * @param a the text A which should be compared - * @param b the text B which should be compared - */ - public MyersDiff(SequenceComparator cmp, S a, S b) { + private MyersDiff(SequenceComparator cmp, S a, S b) { this.cmp = cmp; this.a = a; this.b = b; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java index a2a0ee6ab..d945f1965 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java @@ -90,9 +90,9 @@ public final class MergeAlgorithm { sequences.add(ours); sequences.add(theirs); MergeResult result = new MergeResult(sequences); - EditList oursEdits = new MyersDiff(cmp, base, ours).getEdits(); + EditList oursEdits = MyersDiff.INSTANCE.diff(cmp, base, ours); Iterator baseToOurs = oursEdits.iterator(); - EditList theirsEdits = new MyersDiff(cmp, base, theirs).getEdits(); + EditList theirsEdits = MyersDiff.INSTANCE.diff(cmp, base, theirs); Iterator baseToTheirs = theirsEdits.iterator(); int current = 0; // points to the next line (first line is 0) of base // which was not handled yet