This commit is contained in:
Tobias Warneke
2021-03-14 22:44:20 +01:00
parent 8d38e1012e
commit 2a1239b3a2
7 changed files with 148 additions and 47 deletions

View File

@@ -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]

View File

@@ -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

View File

@@ -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;
/**

View File

@@ -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));

View File

@@ -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);

View File

@@ -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());
// });
// });
}
}

View File

@@ -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>&lt;</code> or <code>&gt;</code>.
+ * Replaces all opening and closing tags with <code>&lt;</code> or <code>&gt;</code>.
*
* @param str
- * @return
+ * @return str with some HTML meta characters escaped.
*/
public static String htmlEntites(String str) {
return str.replace("<", "&lt;").replace(">", "&gt;");
@@ -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