implemented first version of inline merge diff

This commit is contained in:
wumpz
2017-04-04 15:14:20 +02:00
parent 4c03ec4caf
commit e3924b032f
2 changed files with 157 additions and 141 deletions

View File

@@ -56,6 +56,7 @@ public class DiffRowGenerator {
private final String inlineNewCssClass;
private final int columnWidth;
private final Equalizer<String> 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<String>() {
@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<DiffRow> generateDiffRows(final List<String> original, Patch<String> patch) throws DiffException {
// normalize the lines (expand tabs, escape html entities)
//List<String> original = originalText;
//List<String> 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<DiffRow> diffRows = new ArrayList<>();
int endPos = 0;
final List<Delta<String>> deltaList = patch.getDeltas();
@@ -233,15 +246,6 @@ public class DiffRowGenerator {
Chunk<String> orig = delta.getOriginal();
Chunk<String> rev = delta.getRevised();
// We should normalize and wrap lines in deltas too.
// TODO: not in Deltas
//orig.setLines(StringUtils.normalize((List<String>) orig.getLines()));
//rev.setLines(StringUtils.normalize((List<String>) rev.getLines()));
//TODO: no we shouldnt: Deltas should not be used for display purposes
//orig.setLines(StringUtils.wrapText((List<String>) orig.getLines(), this.columnWidth));
//rev.setLines(StringUtils.wrapText((List<String>) 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<DiffRow> generateInlineDiffs(Delta<String> delta, boolean mergeToTarget) throws DiffException {
if (delta.getType()!=Delta.DeltaType.CHANGE)
throw new IllegalArgumentException("only for change deltas allowed");
// List<String> orig = delta.getOriginal().getLines();
// List<String> rev = delta.getRevised().getLines();
// LinkedList<String> origList = new LinkedList<>();
// for (Character character : String.join("\n", orig).toCharArray()) {
// origList.add(character.toString());
// }
// LinkedList<String> revList = new LinkedList<>();
// for (Character character : String.join("\n", rev).toCharArray()) {
// revList.add(character.toString());
// }
// List<Delta<String>> inlineDeltas = DiffUtils.diff(origList, revList).getDeltas();
Patch<String> patch = DiffUtils.diffInline(String.join("\n", delta.getOriginal().getLines()), String.join("\n", delta.getRevised().getLines()));
if (patch.getDeltas().size() < 3) {
List<Delta<String>> deltas = new ArrayList<>(patch.getDeltas());
Collections.reverse(deltas);
for (Delta<String> inlineDelta : deltas) {
System.out.println(inlineDelta);
private List<DiffRow> generateInlineDiffs(Delta<String> delta) throws DiffException {
List<String> orig = StringUtils.normalize(delta.getOriginal().getLines());
List<String> rev = StringUtils.normalize(delta.getRevised().getLines());
LinkedList<String> origList = new LinkedList<>();
for (Character character : String.join("\n", orig).toCharArray()) {
origList.add(character.toString());
}
LinkedList<String> revList = new LinkedList<>();
for (Character character : String.join("\n", rev).toCharArray()) {
revList.add(character.toString());
}
List<Delta<String>> inlineDeltas = DiffUtils.diff(origList, revList).getDeltas();
Collections.reverse(inlineDeltas);
for (Delta<String> inlineDelta : inlineDeltas) {
Chunk<String> inlineOrig = inlineDelta.getOriginal();
Chunk<String> 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<String> inlineDelta : inlineDeltas) {
// Chunk<String> inlineOrig = inlineDelta.getOriginal();
// Chunk<String> 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<String> original = Arrays.asList(origResult.toString().split("\n"));
List<String> revised = Arrays.asList(revResult.toString().split("\n"));
List<DiffRow> 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<String> wrapInTag(LinkedList<String> sequence, int startPosition,
public static void wrapInTag(LinkedList<String> sequence, int startPosition,
int endPosition, String tag, String cssClass) {
LinkedList<String> result = (LinkedList<String>) 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("</");
tagBuilder.append(tag);
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 "</" + tag + ">";
}
tagBuilder.delete(0, tagBuilder.length());
tagBuilder.append("</");
tagBuilder.append(tag);
tagBuilder.append(">");
String endTag = tagBuilder.toString();
return startTag + line + endTag;
private static String createOpenTag(String tag, String cssClass) {
return "<" + tag + (cssClass != null ? " class=\"" + cssClass + "\"" : "") + ">";
}
}

View File

@@ -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<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
print(rows);
assertEquals(3, rows.size());
assertEquals("[CHANGE,anything<span class=\"editOldInline\"> </span>,anything]", rows.get(0).toString());
assertEquals("[CHANGE,<span class=\"editOldInline\"> </span>,]", 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<DiffRow> rows = generator.generateDiffRows(Arrays.asList("Test"),Arrays.asList("ester"));
print(rows);
assertEquals(1, rows.size());
assertEquals("[CHANGE,<span class=\"editOldInline\">T</span>est<span class=\"editNewInline\">er</span>,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<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
print(rows);
assertEquals(6, rows.size());
assertEquals("[CHANGE,<span class=\"editOldInline\">test,anything]", rows.get(0).toString());
assertEquals("[CHANGE,</span>anything<span class=\"editOldInline\"> </span>,]", rows.get(1).toString());
assertEquals("[CHANGE,<span class=\"editOldInline\"> </span>,]", rows.get(2).toString());
assertEquals("[EQUAL,other,other]", rows.get(3).toString());
assertEquals("[INSERT,<span class=\"editNewInline\">test</span>,test]", rows.get(4).toString());
assertEquals("[INSERT,<span class=\"editNewInline\">test2</span>,test2]", rows.get(5).toString());
}
}