diff --git a/src/main/java/io/reflectoring/diffparser/api/UnifiedDiffParser.java b/src/main/java/io/reflectoring/diffparser/api/UnifiedDiffParser.java index 059c051..ae775b2 100644 --- a/src/main/java/io/reflectoring/diffparser/api/UnifiedDiffParser.java +++ b/src/main/java/io/reflectoring/diffparser/api/UnifiedDiffParser.java @@ -67,6 +67,9 @@ public List parse(InputStream in) { case INITIAL: // nothing to do break; + case END_HEADER: + parsedDiffs.add(currentDiff); + currentDiff = new Diff(); case HEADER: parseHeader(currentDiff, currentLine); break; @@ -101,7 +104,7 @@ public List parse(InputStream in) { } private void parseNeutralLine(Diff currentDiff, String currentLine) { - Line line = new Line(Line.LineType.NEUTRAL, currentLine); + Line line = new Line(Line.LineType.NEUTRAL, currentLine.startsWith(" ") ? currentLine.substring(1) : currentLine); currentDiff.getLatestHunk().getLines().add(line); } diff --git a/src/main/java/io/reflectoring/diffparser/unified/ParserState.java b/src/main/java/io/reflectoring/diffparser/unified/ParserState.java index b695ecb..9cad9a1 100644 --- a/src/main/java/io/reflectoring/diffparser/unified/ParserState.java +++ b/src/main/java/io/reflectoring/diffparser/unified/ParserState.java @@ -61,6 +61,24 @@ public ParserState nextState(ParseWindow window) { } }, + /** + * The parser is in this state if it is currently parsing a header line, + * which immediately followed another diff, with the "end" diff delimiter. + */ + END_HEADER { + @Override + public ParserState nextState(ParseWindow window) { + String line = window.getFocusLine(); + if (matchesFromFilePattern(line)) { + logTransition(line, HEADER, FROM_FILE); + return FROM_FILE; + } else { + logTransition(line, HEADER, HEADER); + return HEADER; + } + } + }, + /** * The parser is in this state if it is currently parsing the line containing the "from" file. *

@@ -142,6 +160,9 @@ public ParserState nextState(ParseWindow window) { } else if (matchesEndPattern(line, window)) { logTransition(line, FROM_LINE, END); return END; + } else if (matchesHeaderPattern(line, window)) { + logTransition(line, FROM_LINE, END_HEADER); + return END_HEADER; } else if (matchesHunkStartPattern(line)) { logTransition(line, FROM_LINE, HUNK_START); return HUNK_START; @@ -172,6 +193,9 @@ public ParserState nextState(ParseWindow window) { } else if (matchesEndPattern(line, window)) { logTransition(line, TO_LINE, END); return END; + } else if (matchesHeaderPattern(line, window)) { + logTransition(line, TO_LINE, END_HEADER); + return END_HEADER; } else if (matchesHunkStartPattern(line)) { logTransition(line, TO_LINE, HUNK_START); return HUNK_START; @@ -199,6 +223,9 @@ public ParserState nextState(ParseWindow window) { } else if (matchesEndPattern(line, window)) { logTransition(line, NEUTRAL_LINE, END); return END; + } else if (matchesHeaderPattern(line, window)) { + logTransition(line, NEUTRAL_LINE, END_HEADER); + return END_HEADER; } else if (matchesHunkStartPattern(line)) { logTransition(line, NEUTRAL_LINE, HUNK_START); return HUNK_START; @@ -217,8 +244,8 @@ public ParserState nextState(ParseWindow window) { @Override public ParserState nextState(ParseWindow window) { String line = window.getFocusLine(); - logTransition(line, END, INITIAL); - return INITIAL; + logTransition(line, END, HEADER); + return HEADER; } }; @@ -272,6 +299,12 @@ protected boolean matchesEndPattern(String line, ParseWindow window) { // We found another newline after the current newline without a start of a new diff in between. That makes the // current line just a newline within the current diff. return false; + } else if (matchesHunkStartPattern(futureLine) || + matchesFromLinePattern(futureLine) || + matchesToLinePattern(futureLine)) { + // We found an added/removed line, or another hunk start - this + // was just a neutral line in the middle of a hunk. + return false; } else { i++; } @@ -279,17 +312,19 @@ protected boolean matchesEndPattern(String line, ParseWindow window) { // We reached the end of the stream. return true; } else { - // some diff tools like "svn diff" do not put an empty line between two diffs - // we add that empty line and call the method again - String nextFromFileLine = window.getFutureLine(3); - if(nextFromFileLine != null && matchesFromFilePattern(nextFromFileLine)){ - window.addLine(1, ""); - return matchesEndPattern(line, window); - }else{ - return false; - } + return false; } } + protected boolean matchesHeaderPattern(String line, ParseWindow window) { + // some diff tools like "svn diff" do not put an empty line between two diffs + // we add that empty line and call the method again + String nextFromFileLine = window.getFutureLine(2); + if(nextFromFileLine != null && matchesFromFilePattern(nextFromFileLine)){ + return matchesEndPattern("", window); + }else{ + return false; + } + } } diff --git a/src/test/java/io/reflectoring/diffparser/unified/GitDiffTest.java b/src/test/java/io/reflectoring/diffparser/unified/GitDiffTest.java index dd09b06..bbf2461 100644 --- a/src/test/java/io/reflectoring/diffparser/unified/GitDiffTest.java +++ b/src/test/java/io/reflectoring/diffparser/unified/GitDiffTest.java @@ -8,7 +8,10 @@ import io.reflectoring.diffparser.api.model.Diff; import io.reflectoring.diffparser.api.model.Hunk; import io.reflectoring.diffparser.api.model.Line; +import io.reflectoring.diffparser.api.model.Range; import java.io.InputStream; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.testng.annotations.Test; @@ -29,25 +32,181 @@ public void testParse() { // then assertNotNull(diffs); - assertEquals(3, diffs.size()); - - Diff diff1 = diffs.get(0); - assertEquals("a/diffparser/pom.xml", diff1.getFromFileName()); - assertEquals("b/diffparser/pom.xml", diff1.getToFileName()); - assertEquals(2, diff1.getHunks().size()); - - List headerLines = diff1.getHeaderLines(); - assertEquals(2, headerLines.size()); - - Hunk hunk1 = diff1.getHunks().get(0); - assertEquals(6, hunk1.getFromFileRange().getLineStart()); - assertEquals(7, hunk1.getFromFileRange().getLineCount()); - assertEquals(6, hunk1.getToFileRange().getLineStart()); - assertEquals(7, hunk1.getToFileRange().getLineCount()); - - List lines = hunk1.getLines(); - assertEquals(8, lines.size()); - assertEquals(Line.LineType.FROM, lines.get(3).getLineType()); - assertEquals(Line.LineType.TO, lines.get(4).getLineType()); + + List expected = Arrays.asList( + createDiff(Arrays.asList("diff --git a/diffparser/pom.xml b/diffparser/pom.xml", + "index 5809534..4f4147a 100644"), + "a/diffparser/pom.xml", + "b/diffparser/pom.xml", + createHunk(new Range(6, 7), + new Range(6, 7), + new Line(Line.LineType.NEUTRAL, ""), + new Line(Line.LineType.NEUTRAL, " org.wickedsource"), + new Line(Line.LineType.NEUTRAL, " diffparser"), + new Line(Line.LineType.FROM, " 1.1-SNAPSHOT"), + new Line(Line.LineType.TO, " 1.0"), + new Line(Line.LineType.NEUTRAL, " jar"), + new Line(Line.LineType.NEUTRAL, " diffparser"), + new Line(Line.LineType.NEUTRAL, " Parse textual diffs with Java.") + ), + createHunk(new Range(101, 19), + new Range(101, 6), + new Line(Line.LineType.NEUTRAL, " "), + new Line(Line.LineType.NEUTRAL, " "), + new Line(Line.LineType.NEUTRAL, " "), + new Line(Line.LineType.FROM, " "), + new Line(Line.LineType.FROM, " org.apache.maven.plugins"), + new Line(Line.LineType.FROM, " maven-gpg-plugin"), + new Line(Line.LineType.FROM, " "), + new Line(Line.LineType.FROM, " "), + new Line(Line.LineType.FROM, " sign-artifacts"), + new Line(Line.LineType.FROM, " verify"), + new Line(Line.LineType.FROM, " "), + new Line(Line.LineType.FROM, " sign"), + new Line(Line.LineType.FROM, " "), + new Line(Line.LineType.FROM, " "), + new Line(Line.LineType.FROM, " "), + new Line(Line.LineType.FROM, " "), + new Line(Line.LineType.NEUTRAL, " "), + new Line(Line.LineType.NEUTRAL, " ")//, +// new Line(Line.LineType.NEUTRAL, "") //TODO: the last neutral line (empty, prefixes with a space) is interpreted as a separator between diffs, rather than as a neutral line + ) + ), + createDiff(Arrays.asList("diff --git a/diffparser/src/main/java/org/wickedsource/diffparser/api/UnifiedDiffParser.java b/diffparser/src/main/java/org/wickedsource/diffparser/api/UnifiedDiffParser.java", + "index 0b53136..4dcdc64 100644"), + "a/diffparser/src/main/java/org/wickedsource/diffparser/api/UnifiedDiffParser.java", + "b/diffparser/src/main/java/org/wickedsource/diffparser/api/UnifiedDiffParser.java", + createHunk(new Range(79, 7), + new Range(79, 7), + new Line(Line.LineType.NEUTRAL, " parseHunkStart(currentDiff, currentLine);"), + new Line(Line.LineType.NEUTRAL, " break;"), + new Line(Line.LineType.NEUTRAL, " case FROM_LINE:"), + new Line(Line.LineType.FROM, " parseFromLine(currentDiff, currentLine);"), + new Line(Line.LineType.TO, " parseFromLime(currentDiff, currentLine);"), + new Line(Line.LineType.NEUTRAL, " break;"), + new Line(Line.LineType.NEUTRAL, " case TO_LINE:"), + new Line(Line.LineType.NEUTRAL, " parseToLine(currentDiff, currentLine);") + ), + createHunk(new Range(109, 7), + new Range(109, 7), + new Line(Line.LineType.NEUTRAL, " currentDiff.getLatestHunk().getLines().add(toLine);"), + new Line(Line.LineType.NEUTRAL, " }"), + new Line(Line.LineType.NEUTRAL, ""), + new Line(Line.LineType.FROM, " private void parseFromLine(Diff currentDiff, String currentLine) {"), + new Line(Line.LineType.TO, " private void parseFromLime(Diff currentDiff, String currentLine) {"), + new Line(Line.LineType.NEUTRAL, " Line fromLine = new Line(Line.LineType.FROM, currentLine.substring(1));"), + new Line(Line.LineType.NEUTRAL, " currentDiff.getLatestHunk().getLines().add(fromLine);"), + new Line(Line.LineType.NEUTRAL, " }") + ) + ), + createDiff(Arrays.asList("diff --git a/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java b/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java", + "index d9f7e97..15c23a7 100644"), + "a/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java", + "b/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java", + createHunk(new Range(71, 6), + new Range(71, 7), + new Line(Line.LineType.NEUTRAL, " for (int i = 0; i < numberOfLinesToLoad; i++) {"), + new Line(Line.LineType.NEUTRAL, " String nextLine = getNextLine();"), + new Line(Line.LineType.NEUTRAL, " if (nextLine != null) {"), + new Line(Line.LineType.TO, " nextLine = nextLine.trim();"), + new Line(Line.LineType.NEUTRAL, " lineQueue.addLast(nextLine);"), + new Line(Line.LineType.NEUTRAL, " } else {"), + new Line(Line.LineType.NEUTRAL, " throw new IndexOutOfBoundsException(\"End of stream has been reached!\");") + ), + createHunk(new Range(89, 6), + new Range(90, 7), + new Line(Line.LineType.NEUTRAL, " if (lineQueue.isEmpty()) {"), + new Line(Line.LineType.NEUTRAL, " String nextLine = getNextLine();"), + new Line(Line.LineType.NEUTRAL, " if (nextLine != null) {"), + new Line(Line.LineType.TO, " nextLine = nextLine.trim();"), + new Line(Line.LineType.NEUTRAL, " lineQueue.addLast(nextLine);"), + new Line(Line.LineType.NEUTRAL, " }"), + new Line(Line.LineType.NEUTRAL, " return nextLine;") + ) + ), + createDiff(Arrays.asList("diff --git a/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java b/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java", + "index d9f7e97..15c23a7 100644"), + "a/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java", + "b/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java", + createHunk(new Range(74, 0), + new Range(74, 1), + new Line(Line.LineType.TO, " nextLine = nextLine.trim();") + ), + createHunk(new Range(92, 0), + new Range(93, 1), + new Line(Line.LineType.TO, " nextLine = nextLine.trim();") + ) + ), + createDiff(Arrays.asList("diff --git a/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java b/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java", + "index d9f7e97..15c23a7 100644"), + "a/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java", + "b/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java", + createHunk(new Range(74, 1), + new Range(74, 0), + new Line(Line.LineType.FROM, " nextLine = nextLine.trim();") + ), + createHunk(new Range(92, 1), + new Range(93, 0), + new Line(Line.LineType.FROM, " nextLine = nextLine.trim();") + ) + ) + ); + + assertDiffs(expected, diffs); + } + + private static void assertLine(Line expected, Line actual) { + assertEquals(expected.getLineType(), actual.getLineType()); + assertEquals(expected.getContent(), actual.getContent()); + } + + private static void assertRange(Range expected, Range actual) { + assertEquals(expected.getLineStart(), actual.getLineStart()); + assertEquals(expected.getLineCount(), actual.getLineCount()); + } + + private static void assertHunk(Hunk expected, Hunk actual) { + assertRange(expected.getFromFileRange(), actual.getFromFileRange()); + assertRange(expected.getToFileRange(), actual.getToFileRange()); + assertEquals(expected.getLines().size(), actual.getLines().size()); + for (int i = 0; i < expected.getLines().size(); i++) { + assertLine(expected.getLines().get(i), actual.getLines().get(i)); + } + } + + private static void assertDiff(Diff expected, Diff actual) { + assertEquals(expected.getHeaderLines(), actual.getHeaderLines()); + assertEquals(expected.getFromFileName(), actual.getFromFileName()); + assertEquals(expected.getToFileName(), actual.getToFileName()); + assertEquals(expected.getHunks().size(), actual.getHunks().size()); + for (int i = 0; i < expected.getHunks().size(); i++) { + System.err.println("checking hunk: " + i); + assertHunk(expected.getHunks().get(i), actual.getHunks().get(i)); + } + } + + private static void assertDiffs(List expected, List actual) { + assertEquals(expected.size(), actual.size()); + for (int i = 0; i < expected.size(); i++) { + System.err.println("checking diff: " + i); + assertDiff(expected.get(i), actual.get(i)); + } + } + + private static Hunk createHunk(Range from, Range to, Line... lines) { + Hunk h = new Hunk(); + h.setFromFileRange(from); + h.setToFileRange(to); + h.setLines(Collections.unmodifiableList(Arrays.asList(lines))); + return h; + } + + private static Diff createDiff(List headerLines, String fromFile, String toFile, Hunk... hunks) { + Diff d = new Diff(); + d.setHeaderLines(headerLines); + d.setFromFileName(fromFile); + d.setToFileName(toFile); + d.setHunks(Collections.unmodifiableList(Arrays.asList(hunks))); + return d; } } diff --git a/src/test/resources/org/wickedsource/diffparser/unified/git.diff b/src/test/resources/io/reflectoring/diffparser/unified/git.diff similarity index 73% rename from src/test/resources/org/wickedsource/diffparser/unified/git.diff rename to src/test/resources/io/reflectoring/diffparser/unified/git.diff index badd9f0..7662d95 100644 --- a/src/test/resources/org/wickedsource/diffparser/unified/git.diff +++ b/src/test/resources/io/reflectoring/diffparser/unified/git.diff @@ -73,3 +73,19 @@ index d9f7e97..15c23a7 100644 lineQueue.addLast(nextLine); } return nextLine; +diff --git a/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java b/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java +index d9f7e97..15c23a7 100644 +--- a/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java ++++ b/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java +@@ -74,0 +74,1 @@ public class ResizingParseWindow implements ParseWindow { ++ nextLine = nextLine.trim(); +@@ -92,0 +93,1 @@ public class ResizingParseWindow implements ParseWindow { ++ nextLine = nextLine.trim(); +diff --git a/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java b/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java +index d9f7e97..15c23a7 100644 +--- a/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java ++++ b/diffparser/src/main/java/org/wickedsource/diffparser/unified/ResizingParseWindow.java +@@ -74,1 +74,0 @@ public class ResizingParseWindow implements ParseWindow { +- nextLine = nextLine.trim(); +@@ -92,1 +93,0 @@ public class ResizingParseWindow implements ParseWindow { +- nextLine = nextLine.trim();