diff --git a/src/main/java/difflib/text/DiffRowGenerator.java b/src/main/java/difflib/text/DiffRowGenerator.java index 7f83e2b..ce7b200 100644 --- a/src/main/java/difflib/text/DiffRowGenerator.java +++ b/src/main/java/difflib/text/DiffRowGenerator.java @@ -56,6 +56,7 @@ public class DiffRowGenerator { private final String inlineNewCssClass; private final int columnWidth; private final Equalizer equalizer; + private final boolean mergeInline; /** * This class used for building the DiffRowGenerator. @@ -72,6 +73,7 @@ public class DiffRowGenerator { private String inlineOldCssClass = "editOldInline"; private String inlineNewCssClass = "editNewInline"; private int columnWidth = 80; + private boolean mergeInline = false; private Builder() { } @@ -163,6 +165,12 @@ public class DiffRowGenerator { public DiffRowGenerator build() { return new DiffRowGenerator(this); } + + public Builder mergeInline(boolean mergeInline) { + this.mergeInline = mergeInline; + return this; + } + } public static Builder create() { @@ -171,12 +179,13 @@ public class DiffRowGenerator { private DiffRowGenerator(Builder builder) { showInlineDiffs = builder.showInlineDiffs; - ignoreWhiteSpaces = builder.ignoreWhiteSpaces; // + ignoreWhiteSpaces = builder.ignoreWhiteSpaces; inlineOldTag = builder.inlineOldTag; inlineNewTag = builder.inlineNewTag; inlineOldCssClass = builder.inlineOldCssClass; inlineNewCssClass = builder.inlineNewCssClass; - columnWidth = builder.columnWidth; // + columnWidth = builder.columnWidth; + mergeInline = builder.mergeInline; equalizer = new Equalizer() { @Override public boolean equals(String original, String revised) { @@ -202,9 +211,21 @@ public class DiffRowGenerator { } private DiffRow buildDiffRow(Tag type, String orgline, String newline) { - return new DiffRow(type, - StringUtils.wrapText(StringUtils.normalize(orgline), columnWidth), - StringUtils.wrapText(StringUtils.normalize(newline), columnWidth)); + String wrapOrg = StringUtils.wrapText(StringUtils.normalize(orgline), columnWidth); + if (showInlineDiffs && Tag.DELETE == type) { + wrapOrg = createOpenTag(inlineOldTag, inlineOldCssClass) + wrapOrg + createCloseTag(inlineOldTag); + } + String wrapNew = StringUtils.wrapText(StringUtils.normalize(newline), columnWidth); + if (showInlineDiffs && Tag.INSERT == type) { + wrapOrg = createOpenTag(inlineNewTag, inlineNewCssClass) + wrapNew + createCloseTag(inlineNewTag); + } + return new DiffRow(type, wrapOrg, wrapNew); + } + + private DiffRow buildDiffRowWithoutNormalizing(Tag type, String orgline, String newline) { + return new DiffRow(type, + StringUtils.wrapText(orgline, columnWidth), + StringUtils.wrapText(newline, columnWidth)); } /** @@ -217,14 +238,6 @@ public class DiffRowGenerator { * @return the DiffRows between original and revised texts */ public List generateDiffRows(final List original, Patch patch) throws DiffException { - // normalize the lines (expand tabs, escape html entities) - //List original = originalText; - //List revised = StringUtils.normalize(revisedText); - - // wrap to the column width - // TODO: we want to process original text und not wrapped text - //original = StringUtils.wrapText(original, this.columnWidth); - //revised = StringUtils.wrapText(revised, this.columnWidth); List diffRows = new ArrayList<>(); int endPos = 0; final List> deltaList = patch.getDeltas(); @@ -233,15 +246,6 @@ public class DiffRowGenerator { Chunk orig = delta.getOriginal(); Chunk rev = delta.getRevised(); - // We should normalize and wrap lines in deltas too. - // TODO: not in Deltas - //orig.setLines(StringUtils.normalize((List) orig.getLines())); - //rev.setLines(StringUtils.normalize((List) rev.getLines())); - - //TODO: no we shouldnt: Deltas should not be used for display purposes - //orig.setLines(StringUtils.wrapText((List) orig.getLines(), this.columnWidth)); - //rev.setLines(StringUtils.wrapText((List) rev.getLines(), this.columnWidth)); - // catch the equal prefix for each chunk for (String line : original.subList(endPos, orig.getPosition())) { diffRows.add(buildDiffRow(Tag.EQUAL, line, line)); } @@ -265,23 +269,12 @@ public class DiffRowGenerator { } if (showInlineDiffs) { - diffRows.addAll(generateInlineDiffs(delta, false)); + diffRows.addAll(generateInlineDiffs(delta)); } else { - if (orig.size() == rev.size()) { - for (int j = 0; j < orig.size(); j++) { - diffRows.add(buildDiffRow(Tag.CHANGE, (String) orig.getLines().get(j), - (String) rev.getLines().get(j))); - } - } else if (orig.size() > rev.size()) { - for (int j = 0; j < orig.size(); j++) { - diffRows.add(buildDiffRow(Tag.CHANGE, (String) orig.getLines().get(j), rev - .getLines().size() > j ? (String) rev.getLines().get(j) : "")); - } - } else { - for (int j = 0; j < rev.size(); j++) { - diffRows.add(buildDiffRow(Tag.CHANGE, orig.getLines().size() > j ? (String) orig - .getLines().get(j) : "", (String) rev.getLines().get(j))); - } + for (int j = 0; j < Math.max(orig.size(), rev.size()); j++) { + diffRows.add(buildDiffRow(Tag.CHANGE, + orig.getLines().size() > j ? orig.getLines().get(j) : "", + rev.getLines().size() > j ? rev.getLines().get(j) : "")); } } endPos = orig.last() + 1; @@ -299,60 +292,73 @@ public class DiffRowGenerator { * * @param delta the given delta */ - private List generateInlineDiffs(Delta delta, boolean mergeToTarget) throws DiffException { - if (delta.getType()!=Delta.DeltaType.CHANGE) - throw new IllegalArgumentException("only for change deltas allowed"); -// List orig = delta.getOriginal().getLines(); -// List rev = delta.getRevised().getLines(); -// LinkedList origList = new LinkedList<>(); -// for (Character character : String.join("\n", orig).toCharArray()) { -// origList.add(character.toString()); -// } -// LinkedList revList = new LinkedList<>(); -// for (Character character : String.join("\n", rev).toCharArray()) { -// revList.add(character.toString()); -// } -// List> inlineDeltas = DiffUtils.diff(origList, revList).getDeltas(); - Patch patch = DiffUtils.diffInline(String.join("\n", delta.getOriginal().getLines()), String.join("\n", delta.getRevised().getLines())); - if (patch.getDeltas().size() < 3) { - List> deltas = new ArrayList<>(patch.getDeltas()); - Collections.reverse(deltas); - - for (Delta inlineDelta : deltas) { - System.out.println(inlineDelta); + private List generateInlineDiffs(Delta delta) throws DiffException { + List orig = StringUtils.normalize(delta.getOriginal().getLines()); + List rev = StringUtils.normalize(delta.getRevised().getLines()); + LinkedList origList = new LinkedList<>(); + for (Character character : String.join("\n", orig).toCharArray()) { + origList.add(character.toString()); + } + LinkedList revList = new LinkedList<>(); + for (Character character : String.join("\n", rev).toCharArray()) { + revList.add(character.toString()); + } + List> inlineDeltas = DiffUtils.diff(origList, revList).getDeltas(); + + Collections.reverse(inlineDeltas); + for (Delta inlineDelta : inlineDeltas) { + Chunk inlineOrig = inlineDelta.getOriginal(); + Chunk inlineRev = inlineDelta.getRevised(); + if (inlineDelta instanceof DeleteDelta) { + wrapInTag(origList, inlineOrig.getPosition(), inlineOrig + .getPosition() + + inlineOrig.size() + 1, this.inlineOldTag, this.inlineOldCssClass); + } else if (inlineDelta instanceof InsertDelta) { + if (mergeInline) { + origList.addAll(inlineOrig.getPosition(), + revList.subList(inlineRev.getPosition(), inlineRev.getPosition() + + inlineRev.size())); + wrapInTag(origList, inlineOrig.getPosition(), inlineOrig.getPosition() + + inlineRev.size() + 1, this.inlineNewTag, this.inlineNewCssClass); + } else { + wrapInTag(revList, inlineRev.getPosition(), inlineRev.getPosition() + + inlineRev.size() + 1, this.inlineNewTag, this.inlineNewCssClass); + } + } else if (inlineDelta instanceof ChangeDelta) { + if (mergeInline) { + origList.addAll(inlineOrig.getPosition(), + revList.subList(inlineRev.getPosition(), inlineRev.getPosition() + + inlineRev.size())); + wrapInTag(origList, inlineOrig.getPosition(), inlineOrig.getPosition() + + inlineRev.size() + 1, this.inlineNewTag, this.inlineNewCssClass); + } else { + wrapInTag(revList, inlineRev.getPosition(), inlineRev.getPosition() + + inlineRev.size() + 1, this.inlineNewTag, this.inlineNewCssClass); + } + wrapInTag(origList, inlineOrig.getPosition(), inlineOrig + .getPosition() + + inlineOrig.size() + 1, this.inlineOldTag, this.inlineOldCssClass); } - - - return Collections.EMPTY_LIST; -// Collections.reverse(inlineDeltas); -// for (Delta inlineDelta : inlineDeltas) { -// Chunk inlineOrig = inlineDelta.getOriginal(); -// Chunk inlineRev = inlineDelta.getRevised(); -// if (inlineDelta instanceof DeleteDelta) { -// origList = wrapInTag(origList, inlineOrig.getPosition(), inlineOrig -// .getPosition() -// + inlineOrig.size() + 1, this.inlineOldTag, this.inlineOldCssClass); -// } else if (inlineDelta instanceof InsertDelta) { -// revList = wrapInTag(revList, inlineRev.getPosition(), inlineRev.getPosition() -// + inlineRev.size() + 1, this.inlineNewTag, this.inlineNewCssClass); -// } else if (inlineDelta instanceof ChangeDelta) { -// origList = wrapInTag(origList, inlineOrig.getPosition(), inlineOrig -// .getPosition() -// + inlineOrig.size() + 1, this.inlineOldTag, this.inlineOldCssClass); -// revList = wrapInTag(revList, inlineRev.getPosition(), inlineRev.getPosition() -// + inlineRev.size() + 1, this.inlineNewTag, this.inlineNewCssClass); -// } -// } -// StringBuilder origResult = new StringBuilder(), revResult = new StringBuilder(); -// for (String character : origList) { -// origResult.append(character); -// } -// for (String character : revList) { -// revResult.append(character); -// } -// delta.getOriginal().setLines(Arrays.asList(origResult.toString().split("\n"))); -// delta.getRevised().setLines(Arrays.asList(revResult.toString().split("\n"))); - } else return Collections.emptyList(); + } + StringBuilder origResult = new StringBuilder(); + StringBuilder revResult = new StringBuilder(); + for (String character : origList) { + origResult.append(character); + } + for (String character : revList) { + revResult.append(character); + } + + List original = Arrays.asList(origResult.toString().split("\n")); + List revised = Arrays.asList(revResult.toString().split("\n")); + List diffRows = new ArrayList<>(); + for (int j = 0; j < Math.max(original.size(), revised.size()); j++) { + diffRows. + add(buildDiffRowWithoutNormalizing(Tag.CHANGE, + original.size() > j ? original.get(j) : "", + revised.size() > j ? revised.get(j) : "")); + } + return diffRows; } /** @@ -364,59 +370,17 @@ public class DiffRowGenerator { * @param tag the tag name without angle brackets, just a word * @param cssClass the optional css class */ - public static LinkedList wrapInTag(LinkedList sequence, int startPosition, + public static void wrapInTag(LinkedList sequence, int startPosition, int endPosition, String tag, String cssClass) { - LinkedList result = (LinkedList) sequence.clone(); - StringBuilder tagBuilder = new StringBuilder(); - tagBuilder.append("<"); - tagBuilder.append(tag); - if (cssClass != null) { - tagBuilder.append(" class=\""); - tagBuilder.append(cssClass); - tagBuilder.append("\""); - } - tagBuilder.append(">"); - String startTag = tagBuilder.toString(); - - tagBuilder.delete(0, tagBuilder.length()); - - tagBuilder.append(""); - String endTag = tagBuilder.toString(); - - result.add(startPosition, startTag); - result.add(endPosition, endTag); - return result; + sequence.add(startPosition, createOpenTag(tag, cssClass)); + sequence.add(endPosition, createCloseTag(tag)); } - /** - * Wrap the given line with the given tag - * - * @param line the given line - * @param tag the tag name without angle brackets, just a word - * @param cssClass the optional css class - * @return the wrapped string - */ - public static String wrapInTag(String line, String tag, String cssClass) { - StringBuilder tagBuilder = new StringBuilder(); - tagBuilder.append("<"); - tagBuilder.append(tag); - if (cssClass != null) { - tagBuilder.append(" class=\""); - tagBuilder.append(cssClass); - tagBuilder.append("\""); - } - tagBuilder.append(">"); - String startTag = tagBuilder.toString(); + private static String createCloseTag(String tag) { + return ""; + } - tagBuilder.delete(0, tagBuilder.length()); - - tagBuilder.append(""); - String endTag = tagBuilder.toString(); - - return startTag + line + endTag; + private static String createOpenTag(String tag, String cssClass) { + return "<" + tag + (cssClass != null ? " class=\"" + cssClass + "\"" : "") + ">"; } } diff --git a/src/test/java/difflib/text/DiffRowGeneratorTest.java b/src/test/java/difflib/text/DiffRowGeneratorTest.java index 29cff1b..be4c37b 100644 --- a/src/test/java/difflib/text/DiffRowGeneratorTest.java +++ b/src/test/java/difflib/text/DiffRowGeneratorTest.java @@ -86,4 +86,56 @@ public class DiffRowGeneratorTest { assertEquals("[CHANGE, ,]", rows.get(1).toString()); assertEquals("[EQUAL,other,other]", rows.get(2).toString()); } + + @Test + public void testGeneratorWithInlineMerge() throws DiffException { + String first = "anything \n \nother"; + String second = "anything\n\nother"; + + DiffRowGenerator generator = DiffRowGenerator.create() + .showInlineDiffs(true) + .mergeInline(true) + .build(); + List rows = generator.generateDiffRows(split(first), split(second)); + print(rows); + + assertEquals(3, rows.size()); + assertEquals("[CHANGE,anything ,anything]", rows.get(0).toString()); + assertEquals("[CHANGE, ,]", rows.get(1).toString()); + assertEquals("[EQUAL,other,other]", rows.get(2).toString()); + } + + @Test + public void testGeneratorWithInlineMerge2() throws DiffException { + DiffRowGenerator generator = DiffRowGenerator.create() + .showInlineDiffs(true) + .mergeInline(true) + .build(); + List rows = generator.generateDiffRows(Arrays.asList("Test"),Arrays.asList("ester")); + print(rows); + + assertEquals(1, rows.size()); + assertEquals("[CHANGE,Tester,ester]", rows.get(0).toString()); + } + + @Test + public void testGeneratorWithInlineMerge3() throws DiffException { + String first = "test\nanything \n \nother"; + String second = "anything\n\nother\ntest\ntest2"; + + DiffRowGenerator generator = DiffRowGenerator.create() + .showInlineDiffs(true) + .mergeInline(true) + .build(); + List rows = generator.generateDiffRows(split(first), split(second)); + print(rows); + + assertEquals(6, rows.size()); + assertEquals("[CHANGE,test,anything]", rows.get(0).toString()); + assertEquals("[CHANGE,anything ,]", rows.get(1).toString()); + assertEquals("[CHANGE, ,]", rows.get(2).toString()); + assertEquals("[EQUAL,other,other]", rows.get(3).toString()); + assertEquals("[INSERT,test,test]", rows.get(4).toString()); + assertEquals("[INSERT,test2,test2]", rows.get(5).toString()); + } }