mirror of
https://github.com/java-diff-utils/java-diff-utils.git
synced 2026-03-13 10:11:17 +08:00
Replace Object with generic type T (issue #22).
This commit is contained in:
@@ -21,14 +21,17 @@ import java.util.List;
|
||||
* Describes the change-delta between original and revised texts.
|
||||
*
|
||||
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
|
||||
* @param T The type of the compared elements in the 'lines'.
|
||||
*/
|
||||
public class ChangeDelta extends Delta {
|
||||
public class ChangeDelta<T> extends Delta<T> {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* Creates a change delta with the two given chunks.
|
||||
* @param original The original chunk. Must not be {@code null}.
|
||||
* @param revised The original chunk. Must not be {@code null}.
|
||||
*/
|
||||
public ChangeDelta(Chunk original, Chunk revised) {
|
||||
super(original, revised);
|
||||
public ChangeDelta(Chunk<T> original, Chunk<T>revised) {
|
||||
super(original, revised);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,7 +40,7 @@ public class ChangeDelta extends Delta {
|
||||
* @throws PatchFailedException
|
||||
*/
|
||||
@Override
|
||||
public void applyTo(List<Object> target) throws PatchFailedException {
|
||||
public void applyTo(List<T> target) throws PatchFailedException {
|
||||
verify(target);
|
||||
int position = getOriginal().getPosition();
|
||||
int size = getOriginal().size();
|
||||
@@ -45,7 +48,7 @@ public class ChangeDelta extends Delta {
|
||||
target.remove(position);
|
||||
}
|
||||
int i = 0;
|
||||
for (Object line : getRevised().getLines()) {
|
||||
for (T line : getRevised().getLines()) {
|
||||
target.add(position + i, line);
|
||||
i++;
|
||||
}
|
||||
@@ -55,14 +58,14 @@ public class ChangeDelta extends Delta {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void restore(List<Object> target) {
|
||||
public void restore(List<T> target) {
|
||||
int position = getRevised().getPosition();
|
||||
int size = getRevised().size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
target.remove(position);
|
||||
}
|
||||
int i = 0;
|
||||
for (Object line : getOriginal().getLines()) {
|
||||
for (T line : getOriginal().getLines()) {
|
||||
target.add(position + i, line);
|
||||
i++;
|
||||
}
|
||||
@@ -71,7 +74,7 @@ public class ChangeDelta extends Delta {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void verify(List<?> target) throws PatchFailedException {
|
||||
public void verify(List<T> target) throws PatchFailedException {
|
||||
getOriginal().verify(target);
|
||||
if (getOriginal().getPosition() > target.size()) {
|
||||
throw new PatchFailedException("Incorrect patch for delta: "
|
||||
|
||||
@@ -30,11 +30,12 @@ import java.util.List;
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="dm.naumenko@gmail.com>Dmitry Naumenko</a>
|
||||
* @param T The type of the compared elements in the 'lines'.
|
||||
*/
|
||||
public class Chunk {
|
||||
public class Chunk<T> {
|
||||
|
||||
private final int position;
|
||||
private List<?> lines;
|
||||
private List<T> lines;
|
||||
|
||||
/**
|
||||
* Creates a chunk and saves a copy of affected lines
|
||||
@@ -44,7 +45,7 @@ public class Chunk {
|
||||
* @param lines
|
||||
* the affected lines
|
||||
*/
|
||||
public Chunk(int position, List<?> lines) {
|
||||
public Chunk(int position, List<T> lines) {
|
||||
this.position = position;
|
||||
this.lines = lines;
|
||||
}
|
||||
@@ -57,7 +58,7 @@ public class Chunk {
|
||||
* @param lines
|
||||
* the affected lines
|
||||
*/
|
||||
public Chunk(int position, Object[] lines) {
|
||||
public Chunk(int position, T[] lines) {
|
||||
this.position = position;
|
||||
this.lines = Arrays.asList(lines);
|
||||
}
|
||||
@@ -69,7 +70,7 @@ public class Chunk {
|
||||
* @param target
|
||||
* the sequence to verify against.
|
||||
*/
|
||||
public void verify(List<?> target) throws PatchFailedException {
|
||||
public void verify(List<T> target) throws PatchFailedException {
|
||||
if (last() > target.size()) {
|
||||
throw new PatchFailedException("Incorrect Chunk: the position of chunk > target size");
|
||||
}
|
||||
@@ -88,14 +89,14 @@ public class Chunk {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setLines(List<?> lines) {
|
||||
public void setLines(List<T> lines) {
|
||||
this.lines = lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the affected lines
|
||||
*/
|
||||
public List<?> getLines() {
|
||||
public List<T> getLines() {
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,13 +21,19 @@ import java.util.List;
|
||||
* Describes the delete-delta between original and revised texts.
|
||||
*
|
||||
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
|
||||
* @param T The type of the compared elements in the 'lines'.
|
||||
*/
|
||||
public class DeleteDelta extends Delta {
|
||||
public class DeleteDelta<T> extends Delta<T> {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public DeleteDelta(Chunk original, Chunk revised) {
|
||||
/**
|
||||
* Creates a change delta with the two given chunks.
|
||||
*
|
||||
* @param original
|
||||
* The original chunk. Must not be {@code null}.
|
||||
* @param revised
|
||||
* The original chunk. Must not be {@code null}.
|
||||
*/
|
||||
public DeleteDelta(Chunk<T> original, Chunk<T> revised) {
|
||||
super(original, revised);
|
||||
}
|
||||
|
||||
@@ -37,7 +43,7 @@ public class DeleteDelta extends Delta {
|
||||
* @throws PatchFailedException
|
||||
*/
|
||||
@Override
|
||||
public void applyTo(List<Object> target) throws PatchFailedException {
|
||||
public void applyTo(List<T> target) throws PatchFailedException {
|
||||
verify(target);
|
||||
int position = getOriginal().getPosition();
|
||||
int size = getOriginal().size();
|
||||
@@ -50,9 +56,9 @@ public class DeleteDelta extends Delta {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void restore(List<Object> target) {
|
||||
public void restore(List<T> target) {
|
||||
int position = this.getRevised().getPosition();
|
||||
List<?> lines = this.getOriginal().getLines();
|
||||
List<T> lines = this.getOriginal().getLines();
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
target.add(position + i, lines.get(i));
|
||||
}
|
||||
@@ -64,7 +70,7 @@ public class DeleteDelta extends Delta {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(List<?> target) throws PatchFailedException {
|
||||
public void verify(List<T> target) throws PatchFailedException {
|
||||
getOriginal().verify(target);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,22 +21,42 @@ import java.util.*;
|
||||
* Describes the delta between original and revised texts.
|
||||
*
|
||||
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
|
||||
* @param T The type of the compared elements in the 'lines'.
|
||||
*/
|
||||
public abstract class Delta {
|
||||
private Chunk original;
|
||||
private Chunk revised;
|
||||
public abstract class Delta<T> {
|
||||
|
||||
/** The original chunk. */
|
||||
private Chunk<T> original;
|
||||
|
||||
/** The revised chunk. */
|
||||
private Chunk<T> revised;
|
||||
|
||||
/**
|
||||
* Specifies the type of the delta.
|
||||
*
|
||||
*/
|
||||
public enum TYPE {
|
||||
CHANGE, DELETE, INSERT
|
||||
/** A change in the original. */
|
||||
CHANGE,
|
||||
/** A delete from the original. */
|
||||
DELETE,
|
||||
/** An insert into the original. */
|
||||
INSERT
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the delta for original and revised chunks
|
||||
*
|
||||
* @param original chunk describes the original text
|
||||
* @param revised chunk describes the revised text
|
||||
* @param original Chunk describing the original text. Must not be {@code null}.
|
||||
* @param revised Chunk describing the revised text. Must not be {@code null}.
|
||||
*/
|
||||
public Delta(Chunk original, Chunk revised) {
|
||||
public Delta(Chunk<T> original, Chunk<T> revised) {
|
||||
if (original == null) {
|
||||
throw new IllegalArgumentException("original must not be null");
|
||||
}
|
||||
if (revised == null) {
|
||||
throw new IllegalArgumentException("revised must not be null");
|
||||
}
|
||||
this.original = original;
|
||||
this.revised = revised;
|
||||
}
|
||||
@@ -47,7 +67,7 @@ public abstract class Delta {
|
||||
* @param target the text to patch.
|
||||
* @throws PatchFailedException if the patch cannot be applied.
|
||||
*/
|
||||
public abstract void verify(List<?> target) throws PatchFailedException;
|
||||
public abstract void verify(List<T> target) throws PatchFailedException;
|
||||
|
||||
/**
|
||||
* Applies this delta as the patch for a given target
|
||||
@@ -55,7 +75,7 @@ public abstract class Delta {
|
||||
* @param target the given target
|
||||
* @throws PatchFailedException
|
||||
*/
|
||||
public abstract void applyTo(List<Object> target) throws PatchFailedException;
|
||||
public abstract void applyTo(List<T> target) throws PatchFailedException;
|
||||
|
||||
/**
|
||||
* Cancel this delta for a given revised text. The action is opposite to
|
||||
@@ -63,7 +83,7 @@ public abstract class Delta {
|
||||
*
|
||||
* @param target the given revised text
|
||||
*/
|
||||
public abstract void restore(List<Object> target);
|
||||
public abstract void restore(List<T> target);
|
||||
|
||||
/**
|
||||
* Returns the type of delta
|
||||
@@ -72,38 +92,33 @@ public abstract class Delta {
|
||||
public abstract TYPE getType();
|
||||
|
||||
/**
|
||||
* @return the Chunk describing the original text
|
||||
* @return The Chunk describing the original text.
|
||||
*/
|
||||
public Chunk getOriginal() {
|
||||
public Chunk<T> getOriginal() {
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param original the Chunk describing the original text to set
|
||||
* @param original The Chunk describing the original text to set.
|
||||
*/
|
||||
public void setOriginal(Chunk original) {
|
||||
public void setOriginal(Chunk<T> original) {
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the Chunk describing the revised text
|
||||
* @return The Chunk describing the revised text.
|
||||
*/
|
||||
public Chunk getRevised() {
|
||||
public Chunk<T> getRevised() {
|
||||
return revised;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param revised the Chunk describing the revised text to set
|
||||
* @param revised The Chunk describing the revised text to set.
|
||||
*/
|
||||
public void setRevised(Chunk revised) {
|
||||
public void setRevised(Chunk<T> revised) {
|
||||
this.revised = revised;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
@@ -113,11 +128,6 @@ public abstract class Delta {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
@@ -126,7 +136,7 @@ public abstract class Delta {
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Delta other = (Delta) obj;
|
||||
Delta<T> other = (Delta) obj;
|
||||
if (original == null) {
|
||||
if (other.original != null)
|
||||
return false;
|
||||
|
||||
@@ -5,15 +5,16 @@ import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* @author mksenzov
|
||||
* @param T The type of the compared elements in the 'lines'.
|
||||
*/
|
||||
public class DeltaComparator implements Comparator<Delta>, Serializable {
|
||||
public class DeltaComparator implements Comparator<Delta<?>>, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public static final Comparator<Delta> INSTANCE = new DeltaComparator();
|
||||
public static final Comparator<Delta<?>> INSTANCE = new DeltaComparator();
|
||||
|
||||
private DeltaComparator() {
|
||||
}
|
||||
|
||||
public int compare(final Delta a, final Delta b) {
|
||||
public int compare(final Delta<?> a, final Delta<?> b) {
|
||||
final int posA = a.getOriginal().getPosition();
|
||||
final int posB = b.getOriginal().getPosition();
|
||||
if (posA > posB) {
|
||||
|
||||
@@ -18,29 +18,30 @@ package difflib;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The general interface for computing diffs between two texts
|
||||
* The general interface for computing diffs between two lists of elements of type T.
|
||||
*
|
||||
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
|
||||
* @param T The type of the compared elements in the 'lines'.
|
||||
*/
|
||||
public interface DiffAlgorithm {
|
||||
public interface DiffAlgorithm<T> {
|
||||
|
||||
/**
|
||||
* Computes the difference between the original sequence and the revised
|
||||
* sequence and returns it as a {@link difflib.Patch Patch} object.
|
||||
* sequence and returns it as a {@link Patch} object.
|
||||
*
|
||||
* @param original the original text
|
||||
* @param revised the revised text
|
||||
* @return the patch
|
||||
* @param original The original sequence. Must not be {@code null}.
|
||||
* @param revised The revised sequence. Must not be {@code null}.
|
||||
* @return The patch representing the diff of the given sequences. Never {@code null}.
|
||||
*/
|
||||
public Patch diff(Object[] original, Object[] revised);
|
||||
public Patch<T> diff(T[] original, T[] revised);
|
||||
|
||||
/**
|
||||
* Computes the difference between the original sequence and the revised
|
||||
* sequence and returns it as a {@link difflib.Patch Patch} object.
|
||||
* sequence and returns it as a {@link Patch} object.
|
||||
*
|
||||
* @param original the original text
|
||||
* @param revised the revised text
|
||||
* @return the patch
|
||||
* @param original The original sequence. Must not be {@code null}.
|
||||
* @param revised The revised sequence. Must not be {@code null}.
|
||||
* @return The patch representing the diff of the given sequences. Never {@code null}.
|
||||
*/
|
||||
public Patch diff(List<?> original, List<?> revised);
|
||||
public Patch<T> diff(List<T> original, List<T> revised);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class DiffRowGenerator {
|
||||
private final String InlineOldCssClass;
|
||||
private final String InlineNewCssClass;
|
||||
private final int columnWidth;
|
||||
private final Equalizer equalizer;
|
||||
private final Equalizer<String> equalizer;
|
||||
|
||||
/**
|
||||
* This class used for building the DiffRowGenerator.
|
||||
@@ -166,11 +166,11 @@ public class DiffRowGenerator {
|
||||
InlineOldCssClass = builder.InlineOldCssClass;
|
||||
InlineNewCssClass = builder.InlineNewCssClass;
|
||||
columnWidth = builder.columnWidth; //
|
||||
equalizer = new Equalizer() {
|
||||
public boolean equals(Object original, Object revised) {
|
||||
equalizer = new Equalizer<String>() {
|
||||
public boolean equals(String original, String revised) {
|
||||
if (ignoreWhiteSpaces) {
|
||||
original = ((String)original).trim().replaceAll("\\s+", " ");
|
||||
revised = ((String)revised).trim().replaceAll("\\s+", " ");
|
||||
original = original.trim().replaceAll("\\s+", " ");
|
||||
revised = revised.trim().replaceAll("\\s+", " ");
|
||||
}
|
||||
return original.equals(revised);
|
||||
}
|
||||
@@ -210,7 +210,7 @@ public class DiffRowGenerator {
|
||||
* @return the DiffRows between original and revised texts
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<DiffRow> generateDiffRows(List<String> original, List<String> revised, Patch patch) {
|
||||
public List<DiffRow> generateDiffRows(List<String> original, List<String> revised, Patch<String> patch) {
|
||||
// normalize the lines (expand tabs, escape html entities)
|
||||
original = StringUtills.normalize(original);
|
||||
revised = StringUtills.normalize(revised);
|
||||
@@ -221,11 +221,11 @@ public class DiffRowGenerator {
|
||||
|
||||
List<DiffRow> diffRows = new ArrayList<DiffRow>();
|
||||
int endPos = 0;
|
||||
final List<Delta> deltaList = patch.getDeltas();
|
||||
final List<Delta<String>> deltaList = patch.getDeltas();
|
||||
for (int i = 0; i < deltaList.size(); i++) {
|
||||
Delta delta = deltaList.get(i);
|
||||
Chunk orig = delta.getOriginal();
|
||||
Chunk rev = delta.getRevised();
|
||||
Delta<String> delta = deltaList.get(i);
|
||||
Chunk<String> orig = delta.getOriginal();
|
||||
Chunk<String> rev = delta.getRevised();
|
||||
|
||||
// We should normalize and wrap lines in deltas too.
|
||||
orig.setLines(StringUtills.normalize((List<String>) orig.getLines()));
|
||||
@@ -292,7 +292,7 @@ public class DiffRowGenerator {
|
||||
* @param delta the given delta
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void addInlineDiffs(Delta delta) {
|
||||
private void addInlineDiffs(Delta<String> delta) {
|
||||
List<String> orig = (List<String>) delta.getOriginal().getLines();
|
||||
List<String> rev = (List<String>) delta.getRevised().getLines();
|
||||
LinkedList<String> origList = new LinkedList<String>();
|
||||
@@ -303,12 +303,12 @@ public class DiffRowGenerator {
|
||||
for (Character character : join(rev, "\n").toCharArray()) {
|
||||
revList.add(character.toString());
|
||||
}
|
||||
List<Delta> inlineDeltas = DiffUtils.diff(origList, revList).getDeltas();
|
||||
List<Delta<String>> inlineDeltas = DiffUtils.diff(origList, revList).getDeltas();
|
||||
if (inlineDeltas.size() < 3) {
|
||||
Collections.reverse(inlineDeltas);
|
||||
for (Delta inlineDelta : inlineDeltas) {
|
||||
Chunk inlineOrig = inlineDelta.getOriginal();
|
||||
Chunk inlineRev = inlineDelta.getRevised();
|
||||
for (Delta<String> inlineDelta : inlineDeltas) {
|
||||
Chunk<String> inlineOrig = inlineDelta.getOriginal();
|
||||
Chunk<String> inlineRev = inlineDelta.getRevised();
|
||||
if (inlineDelta.getClass().equals(DeleteDelta.class)) {
|
||||
origList = wrapInTag(origList, inlineOrig.getPosition(), inlineOrig
|
||||
.getPosition()
|
||||
|
||||
@@ -25,329 +25,400 @@ 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
|
||||
* @param T
|
||||
* The type of the compared elements in the 'lines'.
|
||||
*/
|
||||
public class DiffUtils {
|
||||
private static DiffAlgorithm defaultDiffAlgorithm = new MyersDiff();
|
||||
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
|
||||
*
|
||||
* @param original the original text
|
||||
* @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);
|
||||
}
|
||||
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
|
||||
*
|
||||
* @param original the original text
|
||||
* @param revised the revised text
|
||||
* @param equalizer the equalizer object to replace the default compare algorithm (Object.equals)
|
||||
* @return the patch describing the difference between the original and revised texts
|
||||
*/
|
||||
public static Patch diff(List<?> original, List<?> revised, Equalizer equalizer) {
|
||||
if (equalizer != null) {
|
||||
return DiffUtils.diff(original, revised, new MyersDiff(equalizer));
|
||||
}
|
||||
return DiffUtils.diff(original, revised, defaultDiffAlgorithm);
|
||||
}
|
||||
/**
|
||||
* Computes the difference between the original and revised list of elements
|
||||
* with default diff algorithm
|
||||
*
|
||||
* @param original
|
||||
* The original text. Must not be {@code null}.
|
||||
* @param revised
|
||||
* The revised text. Must not be {@code null}.
|
||||
* @return The patch describing the difference between the original and
|
||||
* revised sequences. Never {@code null}.
|
||||
*/
|
||||
public static <T> Patch<T> diff(List<T> original, List<T> revised) {
|
||||
return DiffUtils.diff(original, revised, new MyersDiff<T>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
/**
|
||||
* Computes the difference between the original and revised list of elements
|
||||
* with default diff algorithm
|
||||
*
|
||||
* @param original
|
||||
* The original text. Must not be {@code null}.
|
||||
* @param revised
|
||||
* The revised text. Must not be {@code null}.
|
||||
*
|
||||
* @param equalizer
|
||||
* the equalizer object to replace the default compare algorithm
|
||||
* (Object.equals). If {@code null} the default equalizer of the
|
||||
* default algorithm is used..
|
||||
* @return The patch describing the difference between the original and
|
||||
* revised sequences. Never {@code null}.
|
||||
*/
|
||||
public static <T> Patch<T> diff(List<T> original, List<T> revised,
|
||||
Equalizer<T> equalizer) {
|
||||
if (equalizer != null) {
|
||||
return DiffUtils.diff(original, revised,
|
||||
new MyersDiff<T>(equalizer));
|
||||
}
|
||||
return DiffUtils.diff(original, revised, new MyersDiff<T>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch the original text with given patch
|
||||
*
|
||||
* @param original the original text
|
||||
* @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);
|
||||
}
|
||||
/**
|
||||
* Computes the difference between the original and revised list of elements
|
||||
* with default diff algorithm
|
||||
*
|
||||
* @param original
|
||||
* The original text. Must not be {@code null}.
|
||||
* @param revised
|
||||
* The revised text. Must not be {@code null}.
|
||||
* @param algorithm
|
||||
* The diff algorithm. Must not be {@code null}.
|
||||
* @return The patch describing the difference between the original and
|
||||
* revised sequences. Never {@code null}.
|
||||
*/
|
||||
public static <T> Patch<T> diff(List<T> original, List<T> revised,
|
||||
DiffAlgorithm<T> algorithm) {
|
||||
if (original == null) {
|
||||
throw new IllegalArgumentException("original must not be null");
|
||||
}
|
||||
if (revised == null) {
|
||||
throw new IllegalArgumentException("revised must not be null");
|
||||
}
|
||||
if (algorithm == null) {
|
||||
throw new IllegalArgumentException("algorithm must not be null");
|
||||
}
|
||||
return algorithm.diff(original, revised);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpatch the revised text for a given patch
|
||||
*
|
||||
* @param revised the revised text
|
||||
* @param patch the given patch
|
||||
* @return the original text
|
||||
*/
|
||||
public static List<?> unpatch(List<?> revised, Patch patch) {
|
||||
return patch.restore(revised);
|
||||
}
|
||||
/**
|
||||
* Patch the original text with given patch
|
||||
*
|
||||
* @param original
|
||||
* the original text
|
||||
* @param patch
|
||||
* the given patch
|
||||
* @return the revised text
|
||||
* @throws PatchFailedException
|
||||
* if can't apply patch
|
||||
*/
|
||||
public static <T> List<T> patch(List<T> original, Patch<T> patch)
|
||||
throws PatchFailedException {
|
||||
return patch.applyTo(original);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given text in unified format and creates the list of deltas for it.
|
||||
*
|
||||
* @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();
|
||||
/**
|
||||
* Unpatch the revised text for a given patch
|
||||
*
|
||||
* @param revised
|
||||
* the revised text
|
||||
* @param patch
|
||||
* the given patch
|
||||
* @return the original text
|
||||
*/
|
||||
public static <T> List<T> unpatch(List<T> revised, Patch<T> patch) {
|
||||
return patch.restore(revised);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (line.startsWith("+++")) {
|
||||
inPrelude = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Matcher m = unifiedDiffChunkRe.matcher(line);
|
||||
if (m.find()) {
|
||||
// Process the lines in the previous chunk
|
||||
if (rawChunk.size() != 0) {
|
||||
List<String> oldChunkLines = new ArrayList<String>();
|
||||
List<String> newChunkLines = new ArrayList<String>();
|
||||
/**
|
||||
* Parse the given text in unified format and creates the list of deltas for
|
||||
* it.
|
||||
*
|
||||
* @param diff
|
||||
* the text in unified format
|
||||
* @return the patch with deltas.
|
||||
*/
|
||||
public static Patch<String> parseUnifiedDiff(List<String> diff) {
|
||||
boolean inPrelude = true;
|
||||
List<String[]> rawChunk = new ArrayList<String[]>();
|
||||
Patch<String> patch = new Patch<String>();
|
||||
|
||||
for (Object[] raw_line : rawChunk) {
|
||||
tag = (String) raw_line[0];
|
||||
rest = (String) raw_line[1];
|
||||
if (tag.equals(" ") || tag.equals("-")) {
|
||||
oldChunkLines.add(rest);
|
||||
}
|
||||
if (tag.equals(" ") || tag.equals("+")) {
|
||||
newChunkLines.add(rest);
|
||||
}
|
||||
}
|
||||
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));
|
||||
new_ln = m.group(3) == null ? 1 : Integer.parseInt(m.group(3));
|
||||
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) {
|
||||
if (line.startsWith("+++")) {
|
||||
inPrelude = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Matcher m = unifiedDiffChunkRe.matcher(line);
|
||||
if (m.find()) {
|
||||
// Process the lines in the previous chunk
|
||||
if (rawChunk.size() != 0) {
|
||||
List<String> oldChunkLines = new ArrayList<String>();
|
||||
List<String> newChunkLines = new ArrayList<String>();
|
||||
|
||||
if (old_ln == 0) {
|
||||
old_ln += 1;
|
||||
}
|
||||
if (new_ln == 0) {
|
||||
new_ln += 1;
|
||||
}
|
||||
} else {
|
||||
if (line.length() > 0) {
|
||||
tag = line.substring(0, 1);
|
||||
rest = line.substring(1);
|
||||
if (tag.equals(" ") || tag.equals("+") || tag.equals("-")) {
|
||||
rawChunk.add(new Object[]{tag, rest});
|
||||
}
|
||||
} else {
|
||||
rawChunk.add(new Object[]{" ", ""});
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String[] raw_line : rawChunk) {
|
||||
tag = raw_line[0];
|
||||
rest = raw_line[1];
|
||||
if (tag.equals(" ") || tag.equals("-")) {
|
||||
oldChunkLines.add(rest);
|
||||
}
|
||||
if (tag.equals(" ") || tag.equals("+")) {
|
||||
newChunkLines.add(rest);
|
||||
}
|
||||
}
|
||||
patch.addDelta(new ChangeDelta<String>(new Chunk<String>(
|
||||
old_ln - 1, oldChunkLines), new Chunk<String>(
|
||||
new_ln - 1, newChunkLines)));
|
||||
rawChunk.clear();
|
||||
}
|
||||
// Parse the @@ header
|
||||
old_ln = m.group(1) == null ? 1 : Integer.parseInt(m.group(1));
|
||||
new_ln = m.group(3) == null ? 1 : Integer.parseInt(m.group(3));
|
||||
|
||||
// Process the lines in the last chunk
|
||||
if (rawChunk.size() != 0) {
|
||||
List<String> oldChunkLines = new ArrayList<String>();
|
||||
List<String> newChunkLines = new ArrayList<String>();
|
||||
if (old_ln == 0) {
|
||||
old_ln += 1;
|
||||
}
|
||||
if (new_ln == 0) {
|
||||
new_ln += 1;
|
||||
}
|
||||
} else {
|
||||
if (line.length() > 0) {
|
||||
tag = line.substring(0, 1);
|
||||
rest = line.substring(1);
|
||||
if (tag.equals(" ") || tag.equals("+") || tag.equals("-")) {
|
||||
rawChunk.add(new String[] { tag, rest });
|
||||
}
|
||||
} else {
|
||||
rawChunk.add(new String[] { " ", "" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Object[] raw_line : rawChunk) {
|
||||
tag = (String) raw_line[0];
|
||||
rest = (String) raw_line[1];
|
||||
if (tag.equals(" ") || tag.equals("-")) {
|
||||
oldChunkLines.add(rest);
|
||||
}
|
||||
if (tag.equals(" ") || tag.equals("+")) {
|
||||
newChunkLines.add(rest);
|
||||
}
|
||||
}
|
||||
// Process the lines in the last chunk
|
||||
if (rawChunk.size() != 0) {
|
||||
List<String> oldChunkLines = new ArrayList<String>();
|
||||
List<String> newChunkLines = new ArrayList<String>();
|
||||
|
||||
patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, oldChunkLines), new Chunk(
|
||||
new_ln - 1, newChunkLines)));
|
||||
rawChunk.clear();
|
||||
}
|
||||
for (String[] raw_line : rawChunk) {
|
||||
tag = raw_line[0];
|
||||
rest = raw_line[1];
|
||||
if (tag.equals(" ") || tag.equals("-")) {
|
||||
oldChunkLines.add(rest);
|
||||
}
|
||||
if (tag.equals(" ") || tag.equals("+")) {
|
||||
newChunkLines.add(rest);
|
||||
}
|
||||
}
|
||||
|
||||
return patch;
|
||||
}
|
||||
patch.addDelta(new ChangeDelta<String>(new Chunk<String>(
|
||||
old_ln - 1, oldChunkLines), new Chunk<String>(new_ln - 1,
|
||||
newChunkLines)));
|
||||
rawChunk.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* generateUnifiedDiff takes a Patch and some other arguments, returning the Unified Diff format text representing the Patch.
|
||||
*
|
||||
* @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.
|
||||
* @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) {
|
||||
if (!patch.getDeltas().isEmpty()) {
|
||||
List<String> ret = new ArrayList<String>();
|
||||
ret.add("--- " + original);
|
||||
ret.add("+++ " + revised);
|
||||
return patch;
|
||||
}
|
||||
|
||||
List<Delta> patchDeltas = new ArrayList<Delta>(patch.getDeltas());
|
||||
/**
|
||||
* generateUnifiedDiff takes a Patch and some other arguments, returning the
|
||||
* Unified Diff format text representing the Patch.
|
||||
*
|
||||
* @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.
|
||||
* @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<String> patch,
|
||||
int contextSize) {
|
||||
if (!patch.getDeltas().isEmpty()) {
|
||||
List<String> ret = new ArrayList<String>();
|
||||
ret.add("--- " + original);
|
||||
ret.add("+++ " + revised);
|
||||
|
||||
// code outside the if block also works for single-delta issues.
|
||||
List<Delta> deltas = new ArrayList<Delta>(); // current list of Delta's to process
|
||||
Delta delta = patchDeltas.get(0);
|
||||
deltas.add(delta); // add the first Delta to the current set
|
||||
// if there's more than 1 Delta, we may need to output them together
|
||||
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
|
||||
List<Delta<String>> patchDeltas = new ArrayList<Delta<String>>(
|
||||
patch.getDeltas());
|
||||
|
||||
// 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().size() + contextSize) >=
|
||||
(nextDelta.getOriginal().getPosition() - contextSize)) {
|
||||
deltas.add(nextDelta);
|
||||
} else {
|
||||
// if it isn't, output the current set,
|
||||
// then create a new set and add the current Delta to it.
|
||||
List<String> curBlock = processDeltas(originalLines, deltas, contextSize);
|
||||
ret.addAll(curBlock);
|
||||
deltas.clear();
|
||||
deltas.add(nextDelta);
|
||||
}
|
||||
delta = nextDelta;
|
||||
}
|
||||
// code outside the if block also works for single-delta issues.
|
||||
List<Delta<String>> deltas = new ArrayList<Delta<String>>(); // current
|
||||
// list
|
||||
// of
|
||||
// Delta's to
|
||||
// process
|
||||
Delta<String> delta = patchDeltas.get(0);
|
||||
deltas.add(delta); // add the first Delta to the current set
|
||||
// if there's more than 1 Delta, we may need to output them together
|
||||
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
|
||||
|
||||
}
|
||||
// don't forget to process the last set of Deltas
|
||||
List<String> curBlock = processDeltas(originalLines, deltas, contextSize);
|
||||
ret.addAll(curBlock);
|
||||
return ret;
|
||||
}
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
// Check if the next Delta is too close to the current
|
||||
// position.
|
||||
// And if it is, add it to the current set
|
||||
Delta<String> nextDelta = patchDeltas.get(i);
|
||||
if ((position + delta.getOriginal().size() + contextSize) >= (nextDelta
|
||||
.getOriginal().getPosition() - contextSize)) {
|
||||
deltas.add(nextDelta);
|
||||
} else {
|
||||
// if it isn't, output the current set,
|
||||
// then create a new set and add the current Delta to
|
||||
// it.
|
||||
List<String> curBlock = processDeltas(originalLines,
|
||||
deltas, contextSize);
|
||||
ret.addAll(curBlock);
|
||||
deltas.clear();
|
||||
deltas.add(nextDelta);
|
||||
}
|
||||
delta = nextDelta;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
private static List<String> processDeltas(List<String> origLines, List<Delta> deltas,
|
||||
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;
|
||||
}
|
||||
// don't forget to process the last set of Deltas
|
||||
List<String> curBlock = processDeltas(originalLines, deltas,
|
||||
contextSize);
|
||||
ret.addAll(curBlock);
|
||||
return ret;
|
||||
}
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
|
||||
Delta curDelta = deltas.get(0);
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
private static List<String> processDeltas(List<String> origLines,
|
||||
List<Delta<String>> deltas, 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;
|
||||
|
||||
// NOTE: +1 to overcome the 0-offset Position
|
||||
int origStart = curDelta.getOriginal().getPosition() + 1 - contextSize;
|
||||
if (origStart < 1) {
|
||||
origStart = 1;
|
||||
}
|
||||
Delta<String> curDelta = deltas.get(0);
|
||||
|
||||
int revStart = curDelta.getRevised().getPosition() + 1 - contextSize;
|
||||
if (revStart < 1) {
|
||||
revStart = 1;
|
||||
}
|
||||
// NOTE: +1 to overcome the 0-offset Position
|
||||
int origStart = curDelta.getOriginal().getPosition() + 1 - contextSize;
|
||||
if (origStart < 1) {
|
||||
origStart = 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
|
||||
}
|
||||
int revStart = curDelta.getRevised().getPosition() + 1 - contextSize;
|
||||
if (revStart < 1) {
|
||||
revStart = 1;
|
||||
}
|
||||
|
||||
// output the context before the first Delta
|
||||
for (line = contextStart; line < curDelta.getOriginal().getPosition(); line++) { //
|
||||
buffer.add(" " + origLines.get(line));
|
||||
origTotal++;
|
||||
revTotal++;
|
||||
}
|
||||
// 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 first Delta
|
||||
buffer.addAll(getDeltaText(curDelta));
|
||||
origTotal += curDelta.getOriginal().getLines().size();
|
||||
revTotal += curDelta.getRevised().getLines().size();
|
||||
// output the context before the first Delta
|
||||
for (line = contextStart; line < curDelta.getOriginal().getPosition(); line++) { //
|
||||
buffer.add(" " + origLines.get(line));
|
||||
origTotal++;
|
||||
revTotal++;
|
||||
}
|
||||
|
||||
int deltaIndex = 1;
|
||||
while (deltaIndex < deltas.size()) { // for each of the other Deltas
|
||||
Delta nextDelta = deltas.get(deltaIndex);
|
||||
int intermediateStart = curDelta.getOriginal().getPosition()
|
||||
+ curDelta.getOriginal().getLines().size();
|
||||
for (line = intermediateStart; line < nextDelta.getOriginal().getPosition(); line++) {
|
||||
// output the code between the last Delta and this one
|
||||
buffer.add(" " + origLines.get(line));
|
||||
origTotal++;
|
||||
revTotal++;
|
||||
}
|
||||
buffer.addAll(getDeltaText(nextDelta)); // output the Delta
|
||||
origTotal += nextDelta.getOriginal().getLines().size();
|
||||
revTotal += nextDelta.getRevised().getLines().size();
|
||||
curDelta = nextDelta;
|
||||
deltaIndex++;
|
||||
}
|
||||
// output the first Delta
|
||||
buffer.addAll(getDeltaText(curDelta));
|
||||
origTotal += curDelta.getOriginal().getLines().size();
|
||||
revTotal += curDelta.getRevised().getLines().size();
|
||||
|
||||
// Now output the post-Delta context code, clamping the end of the file
|
||||
contextStart = curDelta.getOriginal().getPosition()
|
||||
+ curDelta.getOriginal().getLines().size();
|
||||
for (line = contextStart; (line < (contextStart + contextSize))
|
||||
&& (line < origLines.size()); line++) {
|
||||
buffer.add(" " + origLines.get(line));
|
||||
origTotal++;
|
||||
revTotal++;
|
||||
}
|
||||
int deltaIndex = 1;
|
||||
while (deltaIndex < deltas.size()) { // for each of the other Deltas
|
||||
Delta<String> nextDelta = deltas.get(deltaIndex);
|
||||
int intermediateStart = curDelta.getOriginal().getPosition()
|
||||
+ curDelta.getOriginal().getLines().size();
|
||||
for (line = intermediateStart; line < nextDelta.getOriginal()
|
||||
.getPosition(); line++) {
|
||||
// output the code between the last Delta and this one
|
||||
buffer.add(" " + origLines.get(line));
|
||||
origTotal++;
|
||||
revTotal++;
|
||||
}
|
||||
buffer.addAll(getDeltaText(nextDelta)); // output the Delta
|
||||
origTotal += nextDelta.getOriginal().getLines().size();
|
||||
revTotal += nextDelta.getRevised().getLines().size();
|
||||
curDelta = nextDelta;
|
||||
deltaIndex++;
|
||||
}
|
||||
|
||||
// Create and insert the block header, conforming to the Unified Diff standard
|
||||
StringBuffer header = new StringBuffer();
|
||||
header.append("@@ -");
|
||||
header.append(origStart);
|
||||
header.append(",");
|
||||
header.append(origTotal);
|
||||
header.append(" +");
|
||||
header.append(revStart);
|
||||
header.append(",");
|
||||
header.append(revTotal);
|
||||
header.append(" @@");
|
||||
buffer.add(0, header.toString());
|
||||
// Now output the post-Delta context code, clamping the end of the file
|
||||
contextStart = curDelta.getOriginal().getPosition()
|
||||
+ curDelta.getOriginal().getLines().size();
|
||||
for (line = contextStart; (line < (contextStart + contextSize))
|
||||
&& (line < origLines.size()); line++) {
|
||||
buffer.add(" " + origLines.get(line));
|
||||
origTotal++;
|
||||
revTotal++;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
// Create and insert the block header, conforming to the Unified Diff
|
||||
// standard
|
||||
StringBuffer header = new StringBuffer();
|
||||
header.append("@@ -");
|
||||
header.append(origStart);
|
||||
header.append(",");
|
||||
header.append(origTotal);
|
||||
header.append(" +");
|
||||
header.append(revStart);
|
||||
header.append(",");
|
||||
header.append(revTotal);
|
||||
header.append(" @@");
|
||||
buffer.add(0, header.toString());
|
||||
|
||||
/**
|
||||
* getDeltaText returns the lines to be added to the Unified Diff text from the Delta parameter
|
||||
*
|
||||
* @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>();
|
||||
for (Object line : delta.getOriginal().getLines()) {
|
||||
buffer.add("-" + line);
|
||||
}
|
||||
for (Object line : delta.getRevised().getLines()) {
|
||||
buffer.add("+" + line);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* getDeltaText returns the lines to be added to the Unified Diff text from
|
||||
* the Delta parameter
|
||||
*
|
||||
* @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<String> delta) {
|
||||
List<String> buffer = new ArrayList<String>();
|
||||
for (String line : delta.getOriginal().getLines()) {
|
||||
buffer.add("-" + line);
|
||||
}
|
||||
for (String line : delta.getRevised().getLines()) {
|
||||
buffer.add("+" + line);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,58 +21,66 @@ import java.util.List;
|
||||
* Describes the add-delta between original and revised texts.
|
||||
*
|
||||
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
|
||||
* @param T
|
||||
* The type of the compared elements in the 'lines'.
|
||||
*/
|
||||
public class InsertDelta extends Delta {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public InsertDelta(Chunk original, Chunk revised) {
|
||||
super(original, revised);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws PatchFailedException
|
||||
*/
|
||||
@Override
|
||||
public void applyTo(List<Object> target) throws PatchFailedException {
|
||||
verify(target);
|
||||
int position = this.getOriginal().getPosition();
|
||||
List<?> lines = this.getRevised().getLines();
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
target.add(position + i, lines.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void restore(List<Object> target) {
|
||||
int position = getRevised().getPosition();
|
||||
int size = getRevised().size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
target.remove(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(List<?> target) throws PatchFailedException {
|
||||
if (getOriginal().getPosition() > target.size()) {
|
||||
throw new PatchFailedException("Incorrect patch for delta: "
|
||||
+ "delta original position > target size");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public TYPE getType() {
|
||||
return Delta.TYPE.INSERT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[InsertDelta, position: " + getOriginal().getPosition() + ", lines: "
|
||||
+ getRevised().getLines() + "]";
|
||||
}
|
||||
public class InsertDelta<T> extends Delta<T> {
|
||||
|
||||
/**
|
||||
* Creates an insert delta with the two given chunks.
|
||||
*
|
||||
* @param original
|
||||
* The original chunk. Must not be {@code null}.
|
||||
* @param revised
|
||||
* The original chunk. Must not be {@code null}.
|
||||
*/
|
||||
public InsertDelta(Chunk<T> original, Chunk<T> revised) {
|
||||
super(original, revised);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @throws PatchFailedException
|
||||
*/
|
||||
@Override
|
||||
public void applyTo(List<T> target) throws PatchFailedException {
|
||||
verify(target);
|
||||
int position = this.getOriginal().getPosition();
|
||||
List<T> lines = this.getRevised().getLines();
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
target.add(position + i, lines.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void restore(List<T> target) {
|
||||
int position = getRevised().getPosition();
|
||||
int size = getRevised().size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
target.remove(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(List<T> target) throws PatchFailedException {
|
||||
if (getOriginal().getPosition() > target.size()) {
|
||||
throw new PatchFailedException("Incorrect patch for delta: "
|
||||
+ "delta original position > target size");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public TYPE getType() {
|
||||
return Delta.TYPE.INSERT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[InsertDelta, position: " + getOriginal().getPosition()
|
||||
+ ", lines: " + getRevised().getLines() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,20 +24,21 @@ import java.util.ListIterator;
|
||||
* Describes the patch holding all deltas between the original and revised texts.
|
||||
*
|
||||
* @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
|
||||
* @param T The type of the compared elements in the 'lines'.
|
||||
*/
|
||||
public class Patch {
|
||||
private List<Delta> deltas = new LinkedList<Delta>();
|
||||
public class Patch<T> {
|
||||
private List<Delta<T>> deltas = new LinkedList<Delta<T>>();
|
||||
|
||||
/**
|
||||
* Apply this patch to the given target
|
||||
* @return the patched text
|
||||
* @throws PatchFailedException if can't apply patch
|
||||
*/
|
||||
public List<?> applyTo(List<?> target) throws PatchFailedException {
|
||||
List<Object> result = new LinkedList<Object>(target);
|
||||
ListIterator<Delta> it = getDeltas().listIterator(deltas.size());
|
||||
public List<T> applyTo(List<T> target) throws PatchFailedException {
|
||||
List<T> result = new LinkedList<T>(target);
|
||||
ListIterator<Delta<T>> it = getDeltas().listIterator(deltas.size());
|
||||
while (it.hasPrevious()) {
|
||||
Delta delta = (Delta) it.previous();
|
||||
Delta<T> delta = (Delta<T>) it.previous();
|
||||
delta.applyTo(result);
|
||||
}
|
||||
return result;
|
||||
@@ -48,11 +49,11 @@ public class Patch {
|
||||
* @param target the given target
|
||||
* @return the restored text
|
||||
*/
|
||||
public List<?> restore(List<?> target) {
|
||||
List<Object> result = new LinkedList<Object>(target);
|
||||
ListIterator<Delta> it = getDeltas().listIterator(deltas.size());
|
||||
public List<T> restore(List<T> target) {
|
||||
List<T> result = new LinkedList<T>(target);
|
||||
ListIterator<Delta<T>> it = getDeltas().listIterator(deltas.size());
|
||||
while (it.hasPrevious()) {
|
||||
Delta delta = (Delta) it.previous();
|
||||
Delta<T> delta = (Delta<T>) it.previous();
|
||||
delta.restore(result);
|
||||
}
|
||||
return result;
|
||||
@@ -62,7 +63,7 @@ public class Patch {
|
||||
* Add the given delta to this patch
|
||||
* @param delta the given delta
|
||||
*/
|
||||
public void addDelta(Delta delta) {
|
||||
public void addDelta(Delta<T> delta) {
|
||||
deltas.add(delta);
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ public class Patch {
|
||||
* Get the list of computed deltas
|
||||
* @return the deltas
|
||||
*/
|
||||
public List<Delta> getDeltas() {
|
||||
public List<Delta<T>> getDeltas() {
|
||||
Collections.sort(deltas, DeltaComparator.INSTANCE);
|
||||
return deltas;
|
||||
}
|
||||
|
||||
@@ -33,10 +33,20 @@ public class StringUtills {
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all tabs with 4 spaces.
|
||||
* @param str The string.
|
||||
* @return
|
||||
*/
|
||||
public static String expandTabs(String str) {
|
||||
return str.replace("\t", " ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all opening an closing tags with <code><</code> or <code>></code>.
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static String htmlEntites(String str) {
|
||||
return str.replace("<", "<").replace(">", ">");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
package difflib.myers;
|
||||
|
||||
public interface Equalizer {
|
||||
public boolean equals(Object original, Object revised);
|
||||
/**
|
||||
* Specifies when two compared elements in the Myers algorithm are equal.
|
||||
*
|
||||
* @param T The type of the compared elements in the 'lines'.
|
||||
*/
|
||||
public interface Equalizer<T> {
|
||||
|
||||
/**
|
||||
* Indicates if two elements are equal according to the diff mechanism.
|
||||
* @param original The original element. Must not be {@code null}.
|
||||
* @param revised The revised element. Must not be {@code null}.
|
||||
* @return Returns true if the elements are equal.
|
||||
*/
|
||||
public boolean equals(T original, T revised);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ package difflib.myers;
|
||||
import difflib.*;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -70,34 +72,46 @@ import java.util.List;
|
||||
* http://www.cs.arizona.edu/people/gene/PAPERS/diff.ps</a></p>
|
||||
*
|
||||
* @author <a href="mailto:juanco@suigeneris.org">Juanco Anez</a>
|
||||
* @param T The type of the compared elements in the 'lines'.
|
||||
*/
|
||||
public class MyersDiff implements DiffAlgorithm {
|
||||
private static final Equalizer DEFAULT_EQUALIZER = new Equalizer() {
|
||||
public boolean equals(Object original, Object revised) {
|
||||
public class MyersDiff<T> implements DiffAlgorithm<T> {
|
||||
|
||||
/** Default equalizer. */
|
||||
private final Equalizer<T> DEFAULT_EQUALIZER = new Equalizer<T>() {
|
||||
public boolean equals(final T original, final T revised) {
|
||||
return original.equals(revised);
|
||||
}
|
||||
};
|
||||
|
||||
private Equalizer equalizer = DEFAULT_EQUALIZER;
|
||||
/** The equalizer. */
|
||||
private final Equalizer<T> equalizer;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs an instance of the Myers differencing algorithm.
|
||||
*/
|
||||
public MyersDiff() {
|
||||
equalizer = DEFAULT_EQUALIZER;
|
||||
}
|
||||
|
||||
public MyersDiff(Equalizer equalizer) {
|
||||
/**
|
||||
* Constructs an instance of the Myers differencing algorithm.
|
||||
* @param equalizer Must not be {@code null}.
|
||||
*/
|
||||
public MyersDiff(final Equalizer<T> equalizer) {
|
||||
if (equalizer == null) {
|
||||
throw new IllegalArgumentException("equalizer must not be null");
|
||||
}
|
||||
this.equalizer = equalizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Return empty diff if get the error while procession the difference.
|
||||
* @return Returns an empty diff if get the error while procession the difference.
|
||||
*/
|
||||
public Patch diff(List<?> original, List<?> revised) {
|
||||
return diff(original.toArray(), revised.toArray());
|
||||
public Patch<T> diff(final T[] original, final T[] revised) {
|
||||
return diff(Arrays.asList(original), Arrays.asList(revised));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,15 +119,21 @@ public class MyersDiff implements DiffAlgorithm {
|
||||
*
|
||||
* Return empty diff if get the error while procession the difference.
|
||||
*/
|
||||
public Patch diff(Object[] orig, Object[] rev) {
|
||||
public Patch<T> diff(final List<T> original, final List<T> revised) {
|
||||
if (original == null) {
|
||||
throw new IllegalArgumentException("original list must not be null");
|
||||
}
|
||||
if (revised == null) {
|
||||
throw new IllegalArgumentException("revised list must not be null");
|
||||
}
|
||||
PathNode path;
|
||||
try {
|
||||
path = buildPath(orig, rev);
|
||||
return buildRevision(path, orig, rev);
|
||||
path = buildPath(original, revised);
|
||||
return buildRevision(path, original, revised);
|
||||
} catch (DifferentiationFailedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return new Patch();
|
||||
return new Patch<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +146,7 @@ public class MyersDiff implements DiffAlgorithm {
|
||||
* @return A minimum {@link PathNode Path} accross the differences graph.
|
||||
* @throws DifferentiationFailedException if a diff path could not be found.
|
||||
*/
|
||||
public PathNode buildPath(Object[] orig, Object[] rev)
|
||||
public PathNode buildPath(final List<T> orig, final List<T> rev)
|
||||
throws DifferentiationFailedException {
|
||||
if (orig == null)
|
||||
throw new IllegalArgumentException("original sequence is null");
|
||||
@@ -134,8 +154,8 @@ public class MyersDiff implements DiffAlgorithm {
|
||||
throw new IllegalArgumentException("revised sequence is null");
|
||||
|
||||
// these are local constants
|
||||
final int N = orig.length;
|
||||
final int M = rev.length;
|
||||
final int N = orig.size();
|
||||
final int M = rev.size();
|
||||
|
||||
final int MAX = N + M + 1;
|
||||
final int size = 1 + 2 * MAX;
|
||||
@@ -168,7 +188,7 @@ public class MyersDiff implements DiffAlgorithm {
|
||||
// orig and rev are zero-based
|
||||
// but the algorithm is one-based
|
||||
// that's why there's no +1 when indexing the sequences
|
||||
while (i < N && j < M && equals(orig[i], rev[j])) {
|
||||
while (i < N && j < M && equals(orig.get(i), rev.get(j))) {
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
@@ -188,7 +208,7 @@ public class MyersDiff implements DiffAlgorithm {
|
||||
throw new DifferentiationFailedException("could not find a diff path");
|
||||
}
|
||||
|
||||
private boolean equals(Object orig, Object rev) {
|
||||
private boolean equals(T orig, T rev) {
|
||||
return equalizer.equals(orig, rev);
|
||||
}
|
||||
|
||||
@@ -202,7 +222,7 @@ public class MyersDiff implements DiffAlgorithm {
|
||||
* @throws DifferentiationFailedException if a {@link Patch} could
|
||||
* not be built from the given path.
|
||||
*/
|
||||
public Patch buildRevision(PathNode path, Object[] orig, Object[] rev) {
|
||||
public Patch<T> buildRevision(PathNode path, List<T> orig, List<T> rev) {
|
||||
if (path == null)
|
||||
throw new IllegalArgumentException("path is null");
|
||||
if (orig == null)
|
||||
@@ -210,7 +230,7 @@ public class MyersDiff implements DiffAlgorithm {
|
||||
if (rev == null)
|
||||
throw new IllegalArgumentException("revised sequence is null");
|
||||
|
||||
Patch patch = new Patch();
|
||||
Patch<T> patch = new Patch<T>();
|
||||
if (path.isSnake())
|
||||
path = path.prev;
|
||||
while (path != null && path.prev != null && path.prev.j >= 0) {
|
||||
@@ -223,15 +243,15 @@ public class MyersDiff implements DiffAlgorithm {
|
||||
int ianchor = path.i;
|
||||
int janchor = path.j;
|
||||
|
||||
Chunk original = new Chunk(ianchor, copyOfRange(orig, ianchor, i));
|
||||
Chunk revised = new Chunk(janchor, copyOfRange(rev, janchor, j));
|
||||
Delta delta = null;
|
||||
Chunk<T> original = new Chunk<T>(ianchor, copyOfRange(orig, ianchor, i));
|
||||
Chunk<T> revised = new Chunk<T>(janchor, copyOfRange(rev, janchor, j));
|
||||
Delta<T> delta = null;
|
||||
if (original.size() == 0 && revised.size() != 0) {
|
||||
delta = new InsertDelta(original, revised);
|
||||
delta = new InsertDelta<T>(original, revised);
|
||||
} else if (original.size() > 0 && revised.size() == 0) {
|
||||
delta = new DeleteDelta(original, revised);
|
||||
delta = new DeleteDelta<T>(original, revised);
|
||||
} else {
|
||||
delta = new ChangeDelta(original, revised);
|
||||
delta = new ChangeDelta<T>(original, revised);
|
||||
}
|
||||
|
||||
patch.addDelta(delta);
|
||||
@@ -241,20 +261,31 @@ public class MyersDiff implements DiffAlgorithm {
|
||||
return patch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new list containing the elements returned by {@link List#subList(int, int)}.
|
||||
* @param original The original sequence. Must not be {@code null}.
|
||||
* @param fromIndex low endpoint (inclusive) of the subList.
|
||||
* @param to high endpoint (exclusive) of the subList.
|
||||
* @return A new list of the specified range within the original list.
|
||||
|
||||
*/
|
||||
private List<T> copyOfRange( final List<T> original, final int fromIndex, final int to ) {
|
||||
return new ArrayList<T>( original.subList( fromIndex, to ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied here from JDK 1.6
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T[] copyOfRange(T[] original, int from, int to) {
|
||||
return copyOfRange(original, from, to, (Class<T[]>) original.getClass());
|
||||
public static <T> T[] copyOfRange2(T[] original, int from, int to) {
|
||||
return copyOfRange2(original, from, to, (Class<T[]>) original.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied here from JDK 1.6
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T, U> T[] copyOfRange(U[] original, int from, int to,
|
||||
public static <T, U> T[] copyOfRange2(U[] original, int from, int to,
|
||||
Class<? extends T[]> newType) {
|
||||
int newLength = to - from;
|
||||
if (newLength < 0)
|
||||
|
||||
@@ -31,7 +31,7 @@ public class DiffRowGeneratorTest extends TestCase {
|
||||
.showInlineDiffs(true)
|
||||
.columnWidth(Integer.MAX_VALUE) // do not wrap
|
||||
.build();
|
||||
List<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
|
||||
List<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
|
||||
print(rows);
|
||||
|
||||
assertEquals(3, rows.size());
|
||||
|
||||
@@ -11,49 +11,49 @@ import java.util.List;
|
||||
public class DiffTest extends TestCase {
|
||||
|
||||
public void testDiff_Insert() {
|
||||
final Patch patch = DiffUtils.diff(Arrays.asList("hhh"), Arrays.asList("hhh", "jjj", "kkk"));
|
||||
final Patch<String> patch = DiffUtils.diff(Arrays.asList("hhh"), Arrays.asList("hhh", "jjj", "kkk"));
|
||||
assertNotNull(patch);
|
||||
assertEquals(1, patch.getDeltas().size());
|
||||
final Delta delta = patch.getDeltas().get(0);
|
||||
final Delta<String> delta = patch.getDeltas().get(0);
|
||||
assertEquals(InsertDelta.class, delta.getClass());
|
||||
assertEquals(new Chunk(1, Collections.EMPTY_LIST), delta.getOriginal());
|
||||
assertEquals(new Chunk(1, Arrays.asList("jjj", "kkk")), delta.getRevised());
|
||||
assertEquals(new Chunk<String>(1, Collections.<String> emptyList()), delta.getOriginal());
|
||||
assertEquals(new Chunk<String>(1, Arrays.asList("jjj", "kkk")), delta.getRevised());
|
||||
}
|
||||
|
||||
public void testDiff_Delete() {
|
||||
final Patch patch = DiffUtils.diff(Arrays.asList("ddd", "fff", "ggg"), Arrays.asList("ggg"));
|
||||
final Patch<String> patch = DiffUtils.diff(Arrays.asList("ddd", "fff", "ggg"), Arrays.asList("ggg"));
|
||||
assertNotNull(patch);
|
||||
assertEquals(1, patch.getDeltas().size());
|
||||
final Delta delta = patch.getDeltas().get(0);
|
||||
final Delta<String> delta = patch.getDeltas().get(0);
|
||||
assertEquals(DeleteDelta.class, delta.getClass());
|
||||
assertEquals(new Chunk(0, Arrays.asList("ddd", "fff")), delta.getOriginal());
|
||||
assertEquals(new Chunk(0, Collections.EMPTY_LIST), delta.getRevised());
|
||||
assertEquals(new Chunk<String>(0, Arrays.asList("ddd", "fff")), delta.getOriginal());
|
||||
assertEquals(new Chunk<String>(0, Collections.<String> emptyList()), delta.getRevised());
|
||||
}
|
||||
|
||||
public void testDiff_Change() {
|
||||
final List<String> changeTest_from = Arrays.asList("aaa", "bbb", "ccc");
|
||||
final List<String> changeTest_to = Arrays.asList("aaa", "zzz", "ccc");
|
||||
|
||||
final Patch patch = DiffUtils.diff(changeTest_from, changeTest_to);
|
||||
final Patch<String> patch = DiffUtils.diff(changeTest_from, changeTest_to);
|
||||
assertNotNull(patch);
|
||||
assertEquals(1, patch.getDeltas().size());
|
||||
final Delta delta = patch.getDeltas().get(0);
|
||||
final Delta<String> delta = patch.getDeltas().get(0);
|
||||
assertEquals(ChangeDelta.class, delta.getClass());
|
||||
assertEquals(new Chunk(1, Arrays.asList("bbb")), delta.getOriginal());
|
||||
assertEquals(new Chunk(1, Arrays.asList("zzz")), delta.getRevised());
|
||||
assertEquals(new Chunk<String>(1, Arrays.asList("bbb")), delta.getOriginal());
|
||||
assertEquals(new Chunk<String>(1, Arrays.asList("zzz")), delta.getRevised());
|
||||
}
|
||||
|
||||
public void testDiff_EmptyList() {
|
||||
final Patch patch = DiffUtils.diff(new ArrayList<String>(), new ArrayList<String>());
|
||||
final Patch<String> patch = DiffUtils.diff(new ArrayList<String>(), new ArrayList<String>());
|
||||
assertNotNull(patch);
|
||||
assertEquals(0, patch.getDeltas().size());
|
||||
}
|
||||
|
||||
public void testDiff_EmptyListWithNonEmpty() {
|
||||
final Patch patch = DiffUtils.diff(new ArrayList<String>(), Arrays.asList("aaa"));
|
||||
final Patch<String> patch = DiffUtils.diff(new ArrayList<String>(), Arrays.asList("aaa"));
|
||||
assertNotNull(patch);
|
||||
assertEquals(1, patch.getDeltas().size());
|
||||
final Delta delta = patch.getDeltas().get(0);
|
||||
final Delta<String> delta = patch.getDeltas().get(0);
|
||||
assertEquals(InsertDelta.class, delta.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ public class ComputeDifference extends Example {
|
||||
List<String> revised = fileToLines(RIVISED);
|
||||
|
||||
// Compute diff. Get the Patch object. Patch is the container for computed deltas.
|
||||
Patch patch = DiffUtils.diff(original, revised);
|
||||
Patch<String> patch = DiffUtils.diff(original, revised);
|
||||
|
||||
for (Delta delta: patch.getDeltas()) {
|
||||
for (Delta<String> delta: patch.getDeltas()) {
|
||||
System.out.println(delta);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user