mirror of
https://github.com/java-diff-utils/java-diff-utils.git
synced 2026-03-13 10:11:17 +08:00
fixes #117
This commit is contained in:
@@ -13,8 +13,9 @@ This project uses a custom versioning scheme (and not [Semantic Versioning](http
|
||||
|
||||
### Changed
|
||||
|
||||
* bugfixing on new UnifiedDiff reader / writer for multifile useage
|
||||
* bugfixing on new UnifiedDiff reader / writer for multifile usage
|
||||
* bugfix for wrong DiffRow type while transforming from a patch that removed a line in one changeset
|
||||
* introduced change position into UnifiedDiff reader
|
||||
|
||||
## [4.9]
|
||||
|
||||
|
||||
@@ -25,10 +25,11 @@ import java.util.Objects;
|
||||
* Holds the information about the part of text involved in the diff process
|
||||
*
|
||||
* <p>
|
||||
* Text is represented as <code>Object[]</code> because the diff engine is capable of handling more
|
||||
* than plain ascci. In fact, arrays or lists of any type that implements
|
||||
* {@link java.lang.Object#hashCode hashCode()} and {@link java.lang.Object#equals equals()}
|
||||
* correctly can be subject to differencing using this library.
|
||||
* Text is represented as <code>Object[]</code> because the diff engine is
|
||||
* capable of handling more than plain ascci. In fact, arrays or lists of any
|
||||
* type that implements {@link java.lang.Object#hashCode hashCode()} and
|
||||
* {@link java.lang.Object#equals equals()} correctly can be subject to
|
||||
* differencing using this library.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="dm.naumenko@gmail.com>Dmitry Naumenko</a>
|
||||
@@ -50,7 +51,7 @@ public final class Chunk<T> implements Serializable {
|
||||
public Chunk(int position, List<T> lines, List<Integer> changePosition) {
|
||||
this.position = position;
|
||||
this.lines = new ArrayList<>(lines);
|
||||
this.changePosition = changePosition;
|
||||
this.changePosition = changePosition != null ? new ArrayList<>(changePosition) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,7 +74,7 @@ public final class Chunk<T> implements Serializable {
|
||||
public Chunk(int position, T[] lines, List<Integer> changePosition) {
|
||||
this.position = position;
|
||||
this.lines = Arrays.asList(lines);
|
||||
this.changePosition = changePosition;
|
||||
this.changePosition = changePosition != null ? new ArrayList<>(changePosition) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,7 +88,8 @@ public final class Chunk<T> implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that this chunk's saved text matches the corresponding text in the given sequence.
|
||||
* Verifies that this chunk's saved text matches the corresponding text in
|
||||
* the given sequence.
|
||||
*
|
||||
* @param target the sequence to verify against.
|
||||
* @throws com.github.difflib.patch.PatchFailedException
|
||||
|
||||
@@ -73,6 +73,30 @@ public final class Patch<T> implements Serializable {
|
||||
throw new PatchFailedException("could not apply patch due to " + verifyChunk.toString());
|
||||
};
|
||||
|
||||
/**
|
||||
* Git like merge conflict output.
|
||||
*/
|
||||
public static final ConflictOutput<String> CONFLICT_PRODUCES_MERGE_CONFLICT = (VerifyChunk verifyChunk, AbstractDelta<String> delta, List<String> result) -> {
|
||||
if (result.size() > delta.getSource().getPosition()) {
|
||||
List<String> orgData = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < delta.getSource().size(); i++) {
|
||||
orgData.add(result.get(delta.getSource().getPosition()));
|
||||
result.remove(delta.getSource().getPosition());
|
||||
}
|
||||
|
||||
orgData.add(0, "<<<<<< HEAD");
|
||||
orgData.add("======");
|
||||
orgData.addAll(delta.getSource().getLines());
|
||||
orgData.add(">>>>>>> PATCH");
|
||||
|
||||
result.addAll(delta.getSource().getPosition(), orgData);
|
||||
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
};
|
||||
|
||||
private ConflictOutput<T> conflictOutput = CONFLICT_PRODUCES_EXCEPTION;
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,7 +53,7 @@ public final class UnifiedDiffReader {
|
||||
private final UnifiedDiffLine RENAME_TO = new UnifiedDiffLine(true, "^rename\\sto\\s(.+)$", this::processRenameTo);
|
||||
|
||||
private final UnifiedDiffLine NEW_FILE_MODE = new UnifiedDiffLine(true, "^new\\sfile\\smode\\s(\\d+)", this::processNewFileMode);
|
||||
|
||||
|
||||
private final UnifiedDiffLine DELETED_FILE_MODE = new UnifiedDiffLine(true, "^deleted\\sfile\\smode\\s(\\d+)", this::processDeletedFileMode);
|
||||
|
||||
private final UnifiedDiffLine CHUNK = new UnifiedDiffLine(false, UNIFIED_DIFF_CHUNK_REGEXP, this::processChunk);
|
||||
@@ -94,9 +94,9 @@ public final class UnifiedDiffReader {
|
||||
if (!CHUNK.validLine(line)) {
|
||||
initFileIfNecessary();
|
||||
while (line != null && !CHUNK.validLine(line)) {
|
||||
if (!processLine(line, DIFF_COMMAND, SIMILARITY_INDEX, INDEX,
|
||||
FROM_FILE, TO_FILE,
|
||||
RENAME_FROM, RENAME_TO,
|
||||
if (!processLine(line, DIFF_COMMAND, SIMILARITY_INDEX, INDEX,
|
||||
FROM_FILE, TO_FILE,
|
||||
RENAME_FROM, RENAME_TO,
|
||||
NEW_FILE_MODE, DELETED_FILE_MODE)) {
|
||||
throw new UnifiedDiffParserException("expected file start line not found");
|
||||
}
|
||||
@@ -117,8 +117,8 @@ public final class UnifiedDiffReader {
|
||||
}
|
||||
}
|
||||
line = READER.readLine();
|
||||
|
||||
if ("\\ No newline at end of file".equals(line)) {
|
||||
|
||||
if ("\\ No newline at end of file".equals(line)) {
|
||||
actualFile.setNoNewLineAtTheEndOfTheFile(true);
|
||||
line = READER.readLine();
|
||||
}
|
||||
@@ -153,11 +153,12 @@ public final class UnifiedDiffReader {
|
||||
private static final Logger LOG = Logger.getLogger(UnifiedDiffReader.class.getName());
|
||||
|
||||
/**
|
||||
* To parse a diff file use this method.
|
||||
* To parse a diff file use this method.
|
||||
*
|
||||
* @param stream This is the diff file data.
|
||||
* @return In a UnifiedDiff structure this diff file data is returned.
|
||||
* @throws IOException
|
||||
* @throws UnifiedDiffParserException
|
||||
* @throws UnifiedDiffParserException
|
||||
*/
|
||||
public static UnifiedDiff parseUnifiedDiff(InputStream stream) throws IOException, UnifiedDiffParserException {
|
||||
UnifiedDiffReader parser = new UnifiedDiffReader(new BufferedReader(new InputStreamReader(stream)));
|
||||
@@ -198,27 +199,35 @@ public final class UnifiedDiffReader {
|
||||
actualFile.setToFile(fromTo[1]);
|
||||
actualFile.setDiffCommand(line);
|
||||
}
|
||||
|
||||
|
||||
private void processSimilarityIndex(MatchResult match, String line) {
|
||||
actualFile.setSimilarityIndex(Integer.valueOf(match.group(1)));
|
||||
}
|
||||
|
||||
private List<String> originalTxt = new ArrayList<>();
|
||||
private List<String> revisedTxt = new ArrayList<>();
|
||||
private List<Integer> addLineIdxList = new ArrayList<>();
|
||||
private List<Integer> delLineIdxList = new ArrayList<>();
|
||||
private int old_ln;
|
||||
private int old_size;
|
||||
private int new_ln;
|
||||
private int new_size;
|
||||
private int delLineIdx = 0;
|
||||
private int addLineIdx = 0;
|
||||
|
||||
private void finalizeChunk() {
|
||||
if (!originalTxt.isEmpty() || !revisedTxt.isEmpty()) {
|
||||
actualFile.getPatch().addDelta(new ChangeDelta<>(new Chunk<>(
|
||||
old_ln - 1, originalTxt), new Chunk<>(
|
||||
new_ln - 1, revisedTxt)));
|
||||
old_ln - 1, originalTxt, delLineIdxList), new Chunk<>(
|
||||
new_ln - 1, revisedTxt, addLineIdxList)));
|
||||
old_ln = 0;
|
||||
new_ln = 0;
|
||||
originalTxt.clear();
|
||||
revisedTxt.clear();
|
||||
addLineIdxList.clear();
|
||||
delLineIdxList.clear();
|
||||
delLineIdx = 0;
|
||||
addLineIdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,16 +235,22 @@ public final class UnifiedDiffReader {
|
||||
String cline = line.substring(1);
|
||||
originalTxt.add(cline);
|
||||
revisedTxt.add(cline);
|
||||
delLineIdx++;
|
||||
addLineIdx++;
|
||||
}
|
||||
|
||||
private void processAddLine(MatchResult match, String line) {
|
||||
String cline = line.substring(1);
|
||||
revisedTxt.add(cline);
|
||||
addLineIdx++;
|
||||
addLineIdxList.add(new_ln - 1 + addLineIdx);
|
||||
}
|
||||
|
||||
private void processDelLine(MatchResult match, String line) {
|
||||
String cline = line.substring(1);
|
||||
originalTxt.add(cline);
|
||||
delLineIdx++;
|
||||
delLineIdxList.add(old_ln - 1 + delLineIdx);
|
||||
}
|
||||
|
||||
private void processChunk(MatchResult match, String chunkStart) {
|
||||
@@ -273,7 +288,7 @@ public final class UnifiedDiffReader {
|
||||
actualFile.setToFile(extractFileName(line));
|
||||
actualFile.setToTimestamp(extractTimestamp(line));
|
||||
}
|
||||
|
||||
|
||||
private void processRenameFrom(MatchResult match, String line) {
|
||||
actualFile.setRenameFrom(match.group(1));
|
||||
}
|
||||
@@ -286,7 +301,7 @@ public final class UnifiedDiffReader {
|
||||
//initFileIfNecessary();
|
||||
actualFile.setNewFileMode(match.group(1));
|
||||
}
|
||||
|
||||
|
||||
private void processDeletedFileMode(MatchResult match, String line) {
|
||||
//initFileIfNecessary();
|
||||
actualFile.setDeletedFileMode(match.group(1));
|
||||
|
||||
@@ -14,7 +14,6 @@ import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.github.difflib.DiffUtils;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PatchTest {
|
||||
|
||||
@@ -89,29 +88,7 @@ public class PatchTest {
|
||||
|
||||
changeTest_from.set(2, "CDC");
|
||||
|
||||
patch.withConflictOutput(new ConflictOutput<String>() {
|
||||
@Override
|
||||
public void processConflict(VerifyChunk verifyChunk, AbstractDelta<String> delta, List<String> result) throws PatchFailedException {
|
||||
if (result.size() > delta.getSource().getPosition()) {
|
||||
List<String> orgData = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < delta.getSource().size(); i++) {
|
||||
orgData.add(result.get(delta.getSource().getPosition()));
|
||||
result.remove(delta.getSource().getPosition());
|
||||
}
|
||||
|
||||
orgData.add(0, "<<<<<< HEAD");
|
||||
orgData.add("======");
|
||||
orgData.addAll(delta.getSource().getLines());
|
||||
orgData.add(">>>>>>> PATCH");
|
||||
|
||||
result.addAll(delta.getSource().getPosition(), orgData);
|
||||
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
}
|
||||
});
|
||||
patch.withConflictOutput(Patch.CONFLICT_PRODUCES_MERGE_CONFLICT);
|
||||
|
||||
try {
|
||||
List<String> data = DiffUtils.patch(changeTest_from, patch);
|
||||
|
||||
@@ -295,8 +295,41 @@ public class UnifiedDiffReaderTest {
|
||||
assertThat(file.getSimilarityIndex()).isEqualTo(87);
|
||||
assertThat(file.getRenameFrom()).isEqualTo("service-type-database/build-db.in");
|
||||
assertThat(file.getRenameTo()).isEqualTo("service-type-database/build-db");
|
||||
|
||||
|
||||
assertThat(file.getFromFile()).isEqualTo("service-type-database/build-db.in");
|
||||
assertThat(file.getToFile()).isEqualTo("service-type-database/build-db");
|
||||
assertThat(file.getToFile()).isEqualTo("service-type-database/build-db");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseIssue117() throws IOException {
|
||||
UnifiedDiff diff = UnifiedDiffReader.parseUnifiedDiff(
|
||||
UnifiedDiffReaderTest.class.getResourceAsStream("problem_diff_issue117.diff"));
|
||||
|
||||
assertThat(diff.getFiles().size()).isEqualTo(2);
|
||||
|
||||
assertThat(diff.getFiles().get(0).getPatch().getDeltas().get(0).getSource().getChangePosition())
|
||||
.containsExactly(24, 27);
|
||||
assertThat(diff.getFiles().get(0).getPatch().getDeltas().get(0).getTarget().getChangePosition())
|
||||
.containsExactly(24, 27);
|
||||
|
||||
assertThat(diff.getFiles().get(0).getPatch().getDeltas().get(1).getSource().getChangePosition())
|
||||
.containsExactly(64);
|
||||
assertThat(diff.getFiles().get(0).getPatch().getDeltas().get(1).getTarget().getChangePosition())
|
||||
.containsExactly(64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74);
|
||||
|
||||
// diff.getFiles().forEach(f -> {
|
||||
// System.out.println("File: " + f.getFromFile());
|
||||
// f.getPatch().getDeltas().forEach(delta -> {
|
||||
//
|
||||
// System.out.println(delta);
|
||||
// System.out.println("Source: ");
|
||||
// System.out.println(delta.getSource().getPosition());
|
||||
// System.out.println(delta.getSource().getChangePosition());
|
||||
//
|
||||
// System.out.println("Target: ");
|
||||
// System.out.println(delta.getTarget().getPosition());
|
||||
// System.out.println(delta.getTarget().getChangePosition());
|
||||
// });
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
diff --git a/java-diff-utils/src/main/java/com/github/difflib/text/StringUtils.java b/java-diff-utils/src/main/java/com/github/difflib/text/StringUtils.java
|
||||
index a142548..b7e3549 100644
|
||||
--- a/java-diff-utils/src/main/java/com/github/difflib/text/StringUtils.java
|
||||
+++ b/java-diff-utils/src/main/java/com/github/difflib/text/StringUtils.java
|
||||
@@ -21,10 +21,10 @@
|
||||
final class StringUtils {
|
||||
|
||||
/**
|
||||
- * Replaces all opening an closing tags with <code><</code> or <code>></code>.
|
||||
+ * Replaces all opening and closing tags with <code><</code> or <code>></code>.
|
||||
*
|
||||
* @param str
|
||||
- * @return
|
||||
+ * @return str with some HTML meta characters escaped.
|
||||
*/
|
||||
public static String htmlEntites(String str) {
|
||||
return str.replace("<", "<").replace(">", ">");
|
||||
@@ -61,7 +61,17 @@ public static String wrapText(String line, int columnWidth) {
|
||||
StringBuilder b = new StringBuilder(line);
|
||||
|
||||
for (int count = 0; length > widthIndex; count++) {
|
||||
- b.insert(widthIndex + delimiter * count, "<br/>");
|
||||
+ int breakPoint = widthIndex + delimiter * count;
|
||||
+ if (Character.isHighSurrogate(b.charAt(breakPoint - 1)) &&
|
||||
+ Character.isLowSurrogate(b.charAt(breakPoint))) {
|
||||
+ // Shift a breakpoint that would split a supplemental code-point.
|
||||
+ breakPoint += 1;
|
||||
+ if (breakPoint == b.length()) {
|
||||
+ // Break before instead of after if this is the last code-point.
|
||||
+ breakPoint -= 2;
|
||||
+ }
|
||||
+ }
|
||||
+ b.insert(breakPoint, "<br/>");
|
||||
widthIndex += columnWidth;
|
||||
}
|
||||
|
||||
diff --git a/java-diff-utils/src/test/java/com/github/difflib/text/StringUtilsTest.java b/java-diff-utils/src/test/java/com/github/difflib/text/StringUtilsTest.java
|
||||
index c4b2acc..6867072 100644
|
||||
--- a/java-diff-utils/src/test/java/com/github/difflib/text/StringUtilsTest.java
|
||||
+++ b/java-diff-utils/src/test/java/com/github/difflib/text/StringUtilsTest.java
|
||||
@@ -49,6 +49,8 @@ public void testWrapText_String_int() {
|
||||
assertEquals("te<br/>st", StringUtils.wrapText("test", 2));
|
||||
assertEquals("tes<br/>t", StringUtils.wrapText("test", 3));
|
||||
assertEquals("test", StringUtils.wrapText("test", 10));
|
||||
+ assertEquals(".\uD800\uDC01<br/>.", StringUtils.wrapText(".\uD800\uDC01.", 2));
|
||||
+ assertEquals("..<br/>\uD800\uDC01", StringUtils.wrapText("..\uD800\uDC01", 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
Reference in New Issue
Block a user