cleanup and fixes: comparator in Patch was broken which caused incorrect delta sorting, Chunk does not store lines as a separate field, in fact it is an attribute of the Chunk.lines collection.Minor cleanup in tests, still more to be done in geenerated udiff tests

This commit is contained in:
shotwave
2011-03-25 21:28:13 +00:00
parent 4f9928e4ef
commit 02ded9901f
12 changed files with 217 additions and 304 deletions

View File

@@ -40,7 +40,7 @@ public class ChangeDelta extends Delta {
public void applyTo(List<Object> target) throws PatchFailedException {
verify(target);
int position = getOriginal().getPosition();
int size = getOriginal().getSize();
int size = getOriginal().size();
for (int i = 0; i < size; i++) {
target.remove(position);
}
@@ -57,7 +57,7 @@ public class ChangeDelta extends Delta {
@Override
public void restore(List<Object> target) {
int position = getRevised().getPosition();
int size = getRevised().getSize();
int size = getRevised().size();
for (int i = 0; i < size; i++) {
target.remove(position);
}

View File

@@ -15,7 +15,8 @@
*/
package difflib;
import java.util.*;
import java.util.Arrays;
import java.util.List;
/**
* Holds the information about the part of text involved in the diff process
@@ -31,8 +32,8 @@ import java.util.*;
* @author <a href="dm.naumenko@gmail.com>Dmitry Naumenko</a>
*/
public class Chunk {
private int position;
private int size;
private final int position;
private List<?> lines;
/**
@@ -40,14 +41,11 @@ public class Chunk {
*
* @param position
* the start position
* @param size
* the size of a Chunk
* @param lines
* the affected lines
*/
public Chunk(int position, int size, List<?> lines) {
public Chunk(int position, List<?> lines) {
this.position = position;
this.size = size;
this.lines = lines;
}
@@ -56,14 +54,11 @@ public class Chunk {
*
* @param position
* the start position
* @param size
* the size of a Chunk
* @param lines
* the affected lines
*/
public Chunk(int position, int size, Object[] lines) {
public Chunk(int position, Object[] lines) {
this.position = position;
this.size = size;
this.lines = Arrays.asList(lines);
}
@@ -78,7 +73,7 @@ public class Chunk {
if (last() > target.size()) {
throw new PatchFailedException("Incorrect Chunk: the position of chunk > target size");
}
for (int i = 0; i < size; i++) {
for (int i = 0; i < size(); i++) {
if (!target.get(position + i).equals(lines.get(i))) {
throw new PatchFailedException(
"Incorrect Chunk: the chunk content doesn't match the target");
@@ -92,50 +87,27 @@ public class Chunk {
public int getPosition() {
return position;
}
/**
* @param position
* the start position to set
*/
public void setPosition(int position) {
this.position = position;
public void setLines(List<?> lines) {
this.lines = lines;
}
/**
* @return the size of Chunk (size of affected lines)
*/
public int getSize() {
return size;
}
/**
* @param size
* the size of affected lines to set
*/
public void setSize(int size) {
this.size = size;
}
/**
* @return the affected lines
*/
public List<?> getLines() {
return lines;
}
/**
* @param lines
* the affected lines to set
*/
public void setLines(List<?> lines) {
this.lines = lines;
public int size() {
return lines.size();
}
/**
* Returns the index of the last line of the chunk.
*/
public int last() {
return getPosition() + getSize() - 1;
return getPosition() + size() - 1;
}
/*
@@ -149,7 +121,7 @@ public class Chunk {
int result = 1;
result = prime * result + ((lines == null) ? 0 : lines.hashCode());
result = prime * result + position;
result = prime * result + size;
result = prime * result + size();
return result;
}
@@ -174,14 +146,12 @@ public class Chunk {
return false;
if (position != other.position)
return false;
if (size != other.size)
return false;
return true;
}
@Override
public String toString() {
return "[position: " + position + ", size: " + size + ", lines: " + lines + "]";
return "[position: " + position + ", size: " + size() + ", lines: " + lines + "]";
}
}

View File

@@ -40,7 +40,7 @@ public class DeleteDelta extends Delta {
public void applyTo(List<Object> target) throws PatchFailedException {
verify(target);
int position = getOriginal().getPosition();
int size = getOriginal().getSize();
int size = getOriginal().size();
for (int i = 0; i < size; i++) {
target.remove(position);
}

View File

@@ -0,0 +1,19 @@
package difflib;
import java.io.Serializable;
import java.util.Comparator;
/**
* @author mksenzov
*/
public class DeltaComparator implements Comparator<Delta>, Serializable {
public static final Comparator<Delta> INSTANCE = new DeltaComparator();
private DeltaComparator() {
}
public int compare(final Delta a, final Delta b) {
return new Integer(a.getOriginal().getPosition()).compareTo(b.getOriginal().getPosition());
}
}

View File

@@ -15,15 +15,10 @@
*/
package difflib;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import difflib.DiffRow.Tag;
import java.util.*;
/**
* This class for generating DiffRows for side-by-sidy view.
* You can customize the way of generating. For example, show inline diffs on not, ignoring
@@ -195,17 +190,18 @@ public class DiffRowGenerator {
@SuppressWarnings("unchecked")
public List<DiffRow> generateDiffRows(List<String> original, List<String> revised, Patch patch) {
// normalize the lines (expand tabs, escape html entities)
original = StringUtills.normalize((List<String>) original);
revised = StringUtills.normalize((List<String>) revised);
original = StringUtills.normalize(original);
revised = StringUtills.normalize(revised);
// wrap to the column width
original = StringUtills.wrapText((List<String>) original, this.columnWidth);
revised = StringUtills.wrapText((List<String>) revised, this.columnWidth);
original = StringUtills.wrapText(original, this.columnWidth);
revised = StringUtills.wrapText(revised, this.columnWidth);
List<DiffRow> diffRows = new ArrayList<DiffRow>();
int endPos = 0;
for (int i = 0; i < patch.getDeltas().size(); i++) {
Delta delta = patch.getDelta(i);
final List<Delta> deltaList = patch.getDeltas();
for (int i = 0; i < deltaList.size(); i++) {
Delta delta = deltaList.get(i);
Chunk orig = delta.getOriginal();
Chunk rev = delta.getRevised();
@@ -243,18 +239,18 @@ public class DiffRowGenerator {
addInlineDiffs(delta);
}
// the changed size is match
if (orig.getSize() == rev.getSize()) {
for (int j = 0; j < orig.getSize(); j++) {
if (orig.size() == rev.size()) {
for (int j = 0; j < orig.size(); j++) {
diffRows.add(new DiffRow(Tag.CHANGE, (String) orig.getLines().get(j),
(String) rev.getLines().get(j)));
}
} else if (orig.getSize() > rev.getSize()) {
for (int j = 0; j < orig.getSize(); j++) {
} else if (orig.size() > rev.size()) {
for (int j = 0; j < orig.size(); j++) {
diffRows.add(new DiffRow(Tag.CHANGE, (String) orig.getLines().get(j), rev
.getLines().size() > j ? (String) rev.getLines().get(j) : ""));
}
} else {
for (int j = 0; j < rev.getSize(); j++) {
for (int j = 0; j < rev.size(); j++) {
diffRows.add(new DiffRow(Tag.CHANGE, orig.getLines().size() > j ? (String) orig
.getLines().get(j) : "", (String) rev.getLines().get(j)));
}
@@ -294,16 +290,16 @@ public class DiffRowGenerator {
if (inlineDelta.getClass().equals(DeleteDelta.class)) {
origList = wrapInTag(origList, inlineOrig.getPosition(), inlineOrig
.getPosition()
+ inlineOrig.getSize() + 1, this.InlineOldTag, this.InlineOldCssClass);
+ inlineOrig.size() + 1, this.InlineOldTag, this.InlineOldCssClass);
} else if (inlineDelta.getClass().equals(InsertDelta.class)) {
revList = wrapInTag(revList, inlineRev.getPosition(), inlineRev.getPosition()
+ inlineRev.getSize() + 1, this.InlineNewTag, this.InlineNewCssClass);
+ inlineRev.size() + 1, this.InlineNewTag, this.InlineNewCssClass);
} else if (inlineDelta.getClass().equals(ChangeDelta.class)) {
origList = wrapInTag(origList, inlineOrig.getPosition(), inlineOrig
.getPosition()
+ inlineOrig.getSize() + 1, this.InlineOldTag, this.InlineOldCssClass);
+ inlineOrig.size() + 1, this.InlineOldTag, this.InlineOldCssClass);
revList = wrapInTag(revList, inlineRev.getPosition(), inlineRev.getPosition()
+ inlineRev.getSize() + 1, this.InlineNewTag, this.InlineNewCssClass);
+ inlineRev.size() + 1, this.InlineNewTag, this.InlineNewCssClass);
}
}
StringBuilder origResult = new StringBuilder(), revResult = new StringBuilder();

View File

@@ -26,74 +26,75 @@ import java.util.regex.Pattern;
/**
* Implements the difference and patching engine
*
*
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
* @version 0.4.1
*/
public class DiffUtils {
private static DiffAlgorithm defaultDiffAlgorithm = new MyersDiff();
private static Pattern unifiedDiffChunkRe =
Pattern.compile("@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@");
private static Pattern unifiedDiffChunkRe =
Pattern.compile("@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@");
/**
* Compute the difference between the original and revised texts with default diff algorithm
*
* Compute the difference between the original and revised texts with default diff algorithm
*
* @param original the original text
* @param revised the revised text
* @return the patch describing the difference between the original and revised texts
* @param revised the revised text
* @return the patch describing the difference between the original and revised texts
*/
public static Patch diff(List<?> original, List<?> revised) {
return DiffUtils.diff(original, revised, defaultDiffAlgorithm);
}
/**
* Compute the difference between the original and revised texts with given diff algorithm
*
* @param original the original text
* @param revised the revised text
* Compute the difference between the original and revised texts with given diff algorithm
*
* @param original the original text
* @param revised the revised text
* @param algorithm the given algorithm
* @return the patch describing the difference between the original and revised texts
*/
public static Patch diff(List<?> original, List<?> revised, DiffAlgorithm algorithm) {
return algorithm.diff(original, revised);
}
/**
* Patch the original text with given patch
*
*
* @param original the original text
* @param patch the given patch
* @param patch the given patch
* @return the revised text
* @throws PatchFailedException if can't apply patch
*/
public static List<?> patch(List<?> original, Patch patch) throws PatchFailedException {
return patch.applyTo(original);
}
/**
* Unpatch the revised text for a given patch
*
*
* @param revised the revised text
* @param patch the given patch
* @param patch the given patch
* @return the original text
*/
public static List<?> unpatch(List<?> revised, Patch patch) {
return patch.restore(revised);
}
/**
* Parse the given text in unified format and creates the list of deltas for it.
*
* @param diff the text in unified format
*
* @param diff the text in unified format
* @return the patch with deltas.
*/
public static Patch parseUnifiedDiff(List<String> diff) {
boolean inPrelude = true;
List<Object[]> rawChunk = new ArrayList<Object[]>();
Patch patch = new Patch();
int old_ln = 0, old_n = 0, new_ln = 0, new_n = 0;
String tag = "", rest = "";
int old_ln = 0, new_ln = 0;
String tag;
String rest;
for (String line : diff) {
// Skip leading lines until after we've seen one starting with '+++'
if (inPrelude) {
@@ -108,7 +109,7 @@ public class DiffUtils {
if (rawChunk.size() != 0) {
List<String> oldChunkLines = new ArrayList<String>();
List<String> newChunkLines = new ArrayList<String>();
for (Object[] raw_line : rawChunk) {
tag = (String) raw_line[0];
rest = (String) raw_line[1];
@@ -119,17 +120,14 @@ public class DiffUtils {
newChunkLines.add(rest);
}
}
patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, old_n, oldChunkLines),
new Chunk(new_ln - 1, new_n, newChunkLines)));
patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, oldChunkLines),
new Chunk(new_ln - 1, newChunkLines)));
rawChunk.clear();
}
// Parse the @@ header
old_ln = m.group(1) == null ? 1 : Integer.parseInt(m.group(1));
old_n = m.group(2) == null ? 1 : Integer.parseInt(m.group(2));
new_ln = m.group(3) == null ? 1 : Integer.parseInt(m.group(3));
new_n = m.group(4) == null ? 1 : Integer.parseInt(m.group(4));
old_ln = Integer.parseInt(m.group(1));
if (old_ln == 0) {
old_ln += 1;
}
@@ -141,19 +139,19 @@ public class DiffUtils {
tag = line.substring(0, 1);
rest = line.substring(1);
if (tag.equals(" ") || tag.equals("+") || tag.equals("-")) {
rawChunk.add(new Object[] { tag, rest });
rawChunk.add(new Object[]{tag, rest});
}
} else {
rawChunk.add(new Object[] {" ", ""});
rawChunk.add(new Object[]{" ", ""});
}
}
}
// Process the lines in the last chunk
if (rawChunk.size() != 0) {
List<String> oldChunkLines = new ArrayList<String>();
List<String> newChunkLines = new ArrayList<String>();
for (Object[] raw_line : rawChunk) {
tag = (String) raw_line[0];
rest = (String) raw_line[1];
@@ -164,38 +162,38 @@ public class DiffUtils {
newChunkLines.add(rest);
}
}
patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, old_n, oldChunkLines), new Chunk(
new_ln - 1, new_n, newChunkLines)));
patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, oldChunkLines), new Chunk(
new_ln - 1, newChunkLines)));
rawChunk.clear();
}
return patch;
}
/**
* generateUnifiedDiff takes a Patch and some other arguments, returning the Unified Diff format text representing the Patch.
* @author Bill James (tankerbay@gmail.com)
*
* @param original - Filename of the original (unrevised file)
* @param revised - Filename of the revised file
*
* @param original - Filename of the original (unrevised file)
* @param revised - Filename of the revised file
* @param originalLines - Lines of the original file
* @param patch - Patch created by the diff() function
* @param contextSize - number of lines of context output around each difference in the file.
* @param patch - Patch created by the diff() function
* @param contextSize - number of lines of context output around each difference in the file.
* @return List of strings representing the Unified Diff representation of the Patch argument.
* @author Bill James (tankerbay@gmail.com)
*/
public static List<String> generateUnifiedDiff(String original, String revised,
List<String> originalLines, Patch patch, int contextSize) {
List<String> originalLines, Patch patch, int contextSize) {
List<String> ret = new ArrayList<String>();
ret.add("--- " + original);
ret.add("+++ " + revised);
if (!patch.getDeltas().isEmpty()) {
// Hmm, I thought the Deltas were sorted already... turns out they're not.
List<Delta> patchDeltas = new ArrayList<Delta>( patch.getDeltas() );
Collections.sort( patchDeltas, new Comparator<Delta>() {
public int compare( Delta a, Delta b ) {
return new Integer(a.getOriginal().getPosition()).compareTo( b.getOriginal().getPosition() );
List<Delta> patchDeltas = new ArrayList<Delta>(patch.getDeltas());
Collections.sort(patchDeltas, new Comparator<Delta>() {
public int compare(Delta a, Delta b) {
return new Integer(a.getOriginal().getPosition()).compareTo(b.getOriginal().getPosition());
}
});
@@ -207,13 +205,13 @@ public class DiffUtils {
if (patchDeltas.size() > 1) {
for (int i = 1; i < patchDeltas.size(); i++) {
int position = delta.getOriginal().getPosition(); // store the current position of
// the first Delta
// the first Delta
// Check if the next Delta is too close to the current position.
// And if it is, add it to the current set
Delta nextDelta = patchDeltas.get(i);
if ((position + delta.getOriginal().getSize() + contextSize) >=
(nextDelta.getOriginal().getPosition() - contextSize)) {
if ((position + delta.getOriginal().size() + contextSize) >=
(nextDelta.getOriginal().getPosition() - contextSize)) {
deltas.add(nextDelta);
} else {
// if it isn't, output the current set,
@@ -233,54 +231,54 @@ public class DiffUtils {
}
return ret;
}
/**
* processDeltas takes a list of Deltas and outputs them together in a single block of Unified-Diff-format text.
* processDeltas takes a list of Deltas and outputs them together in a single block of Unified-Diff-format text.
*
* @param origLines - the lines of the original file
* @param deltas - the Deltas to be output as a single block
* @param contextSize - the number of lines of context to place around block
* @return
* @author Bill James (tankerbay@gmail.com)
*
* @param origLines - the lines of the original file
* @param deltas - the Deltas to be output as a single block
* @param contextSize - the number of lines of context to place around block
* @return
*/
*/
private static List<String> processDeltas(List<String> origLines, List<Delta> deltas,
int contextSize) {
int contextSize) {
List<String> buffer = new ArrayList<String>();
int origTotal = 0; // counter for total lines output from Original
int revTotal = 0; // counter for total lines output from Original
int line;
Delta curDelta = deltas.get(0);
// NOTE: +1 to overcome the 0-offset Position
int origStart = curDelta.getOriginal().getPosition() + 1 - contextSize;
if (origStart < 1) {
origStart = 1;
}
int revStart = curDelta.getRevised().getPosition() + 1 - contextSize;
if (revStart < 1) {
revStart = 1;
}
// find the start of the wrapper context code
int contextStart = curDelta.getOriginal().getPosition() - contextSize;
if (contextStart < 0) {
contextStart = 0; // clamp to the start of the file
}
// output the context before the first Delta
for (line = contextStart; line < curDelta.getOriginal().getPosition(); line++) { //
buffer.add(" " + origLines.get(line));
origTotal++;
revTotal++;
}
// output the first Delta
buffer.addAll(getDeltaText(curDelta));
buffer.addAll(getDeltaText(curDelta));
origTotal += curDelta.getOriginal().getLines().size();
revTotal += curDelta.getRevised().getLines().size();
int deltaIndex = 1;
while (deltaIndex < deltas.size()) { // for each of the other Deltas
Delta nextDelta = deltas.get(deltaIndex);
@@ -298,7 +296,7 @@ public class DiffUtils {
curDelta = nextDelta;
deltaIndex++;
}
// Now output the post-Delta context code, clamping the end of the file
contextStart = curDelta.getOriginal().getPosition()
+ curDelta.getOriginal().getLines().size();
@@ -308,7 +306,7 @@ public class DiffUtils {
origTotal++;
revTotal++;
}
// Create and insert the block header, conforming to the Unified Diff standard
StringBuffer header = new StringBuffer();
header.append("@@ -");
@@ -321,16 +319,16 @@ public class DiffUtils {
header.append(revTotal);
header.append(" @@");
buffer.add(0, header.toString());
return buffer;
}
/**
* getDeltaText returns the lines to be added to the Unified Diff text from the Delta parameter
* @author Bill James (tankerbay@gmail.com)
*
*
* @param delta - the Delta to output
* @return list of String lines of code.
* @author Bill James (tankerbay@gmail.com)
*/
private static List<String> getDeltaText(Delta delta) {
List<String> buffer = new ArrayList<String>();
@@ -342,5 +340,5 @@ public class DiffUtils {
}
return buffer;
}
}

View File

@@ -51,7 +51,7 @@ public class InsertDelta extends Delta {
@Override
public void restore(List<Object> target) {
int position = getRevised().getPosition();
int size = getRevised().getSize();
int size = getRevised().size();
for (int i = 0; i < size; i++) {
target.remove(position);
}

View File

@@ -15,7 +15,10 @@
*/
package difflib;
import java.util.*;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
/**
* Describes the patch holding all deltas between the original and revised texts.
@@ -24,11 +27,9 @@ import java.util.*;
*/
public class Patch {
private List<Delta> deltas = new LinkedList<Delta>();
private boolean isSorted = false;
/**
* Apply this patch to the given target
* @param target
* @return the patched text
* @throws PatchFailedException if can't apply patch
*/
@@ -63,45 +64,14 @@ public class Patch {
*/
public void addDelta(Delta delta) {
deltas.add(delta);
isSorted = false;
}
/**
* @param deltas the deltas to set
*/
public void setDeltas(List<Delta> deltas) {
this.deltas = deltas;
isSorted = false;
}
/**
* Get the list of computed deltas
* @return the deltas
*/
public List<Delta> getDeltas() {
if (!this.isSorted) {
Collections.sort(deltas, new Comparator<Delta>() {
public int compare(Delta d1, Delta d2) {
if (d1.getOriginal().getPosition() > d2.getOriginal().getPosition()) {
return 1;
} else if (d1.getOriginal().getPosition() > d2.getOriginal().getPosition()) {
return -1;
} else {
return 0;
}
}
});
isSorted = true;
}
Collections.sort(deltas, DeltaComparator.INSTANCE);
return deltas;
}
/**
* Get the specific delta from patch deltas
* @param index the index of delta
* @return the needed delta
*/
public Delta getDelta(int index) {
return deltas.get(index);
}
}

View File

@@ -64,8 +64,8 @@ package difflib.myers;
* @version $Revision: 69 $ $Date: 2003-10-13 11:00:44 +0300 (Пнд, 13 Окт 2003) $
*
* @author <a href="mailto:juanco@suigeneris.org">Juanco Anez</a>
* @see Diff
* @see DiffAlgorithm
* @see MyersDiff
* @see difflib.DiffAlgorithm
*/
public class DifferentiationFailedException extends DiffException {
private static final long serialVersionUID = 1L;

View File

@@ -57,17 +57,11 @@
package difflib.myers;
import difflib.*;
import java.lang.reflect.Array;
import java.util.List;
import difflib.ChangeDelta;
import difflib.Chunk;
import difflib.DeleteDelta;
import difflib.Delta;
import difflib.DiffAlgorithm;
import difflib.InsertDelta;
import difflib.Patch;
/**
* A clean-room implementation of <a href="http://www.cs.arizona.edu/people/gene/">
* Eugene Myers</a> differencing algorithm.
@@ -182,13 +176,13 @@ public class MyersDiff implements DiffAlgorithm {
}
/**
* Constructs a {@link Revision} from a difference path.
* Constructs a {@link Patch} from a difference path.
*
* @param path The path.
* @param orig The original sequence.
* @param rev The revised sequence.
* @return A {@link Revision} script corresponding to the path.
* @throws DifferentiationFailedException if a {@link Revision} could
* @return A {@link Patch} script corresponding to the path.
* @throws DifferentiationFailedException if a {@link Patch} could
* not be built from the given path.
*/
public static Patch buildRevision(PathNode path, Object[] orig, Object[] rev) {
@@ -212,12 +206,12 @@ public class MyersDiff implements DiffAlgorithm {
int ianchor = path.i;
int janchor = path.j;
Chunk original = new Chunk(ianchor, i - ianchor, copyOfRange(orig, ianchor, i));
Chunk revised = new Chunk(janchor, j - janchor, copyOfRange(rev, janchor, j));
Chunk original = new Chunk(ianchor, copyOfRange(orig, ianchor, i));
Chunk revised = new Chunk(janchor, copyOfRange(rev, janchor, j));
Delta delta = null;
if (original.getSize() == 0 && revised.getSize() != 0) {
if (original.size() == 0 && revised.size() != 0) {
delta = new InsertDelta(original, revised);
} else if (original.getSize() > 0 && revised.getSize() == 0) {
} else if (original.size() > 0 && revised.size() == 0) {
delta = new DeleteDelta(original, revised);
} else {
delta = new ChangeDelta(original, revised);