Skip to content

Commit 7e8d286

Browse files
committed
fixes #114 - decompressing deltas
1 parent ffd14fb commit 7e8d286

File tree

2 files changed

+115
-40
lines changed

2 files changed

+115
-40
lines changed

java-diff-utils/src/main/java/com/github/difflib/text/DiffRowGenerator.java

Lines changed: 88 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717

1818
import com.github.difflib.DiffUtils;
1919
import com.github.difflib.patch.AbstractDelta;
20+
import com.github.difflib.patch.ChangeDelta;
2021
import com.github.difflib.patch.Chunk;
22+
import com.github.difflib.patch.DeleteDelta;
2123
import com.github.difflib.patch.DeltaType;
24+
import com.github.difflib.patch.InsertDelta;
2225
import com.github.difflib.patch.Patch;
2326
import com.github.difflib.text.DiffRow.Tag;
2427
import java.util.*;
@@ -30,14 +33,16 @@
3033
import static java.util.stream.Collectors.toList;
3134

3235
/**
33-
* This class for generating DiffRows for side-by-sidy view. You can customize the way of
34-
* generating. For example, show inline diffs on not, ignoring white spaces or/and blank lines and
35-
* so on. All parameters for generating are optional. If you do not specify them, the class will use
36-
* the default values.
36+
* This class for generating DiffRows for side-by-sidy view. You can customize
37+
* the way of generating. For example, show inline diffs on not, ignoring white
38+
* spaces or/and blank lines and so on. All parameters for generating are
39+
* optional. If you do not specify them, the class will use the default values.
3740
*
38-
* These values are: showInlineDiffs = false; ignoreWhiteSpaces = true; ignoreBlankLines = true; ...
41+
* These values are: showInlineDiffs = false; ignoreWhiteSpaces = true;
42+
* ignoreBlankLines = true; ...
3943
*
40-
* For instantiating the DiffRowGenerator you should use the its builder. Like in example <code>
44+
* For instantiating the DiffRowGenerator you should use the its builder. Like
45+
* in example <code>
4146
* DiffRowGenerator generator = new DiffRowGenerator.Builder().showInlineDiffs(true).
4247
* ignoreWhiteSpaces(true).columnWidth(100).build();
4348
* </code>
@@ -100,8 +105,8 @@ protected final static List<String> splitStringPreserveDelimiter(String str, Pat
100105
/**
101106
* Wrap the elements in the sequence with the given tag
102107
*
103-
* @param startPosition the position from which tag should start. The counting start from a
104-
* zero.
108+
* @param startPosition the position from which tag should start. The
109+
* counting start from a zero.
105110
* @param endPosition the position before which tag should should be closed.
106111
* @param tagGenerator the tag generator
107112
*/
@@ -187,16 +192,16 @@ private DiffRowGenerator(Builder builder) {
187192
reportLinesUnchanged = builder.reportLinesUnchanged;
188193
lineNormalizer = builder.lineNormalizer;
189194
processDiffs = builder.processDiffs;
190-
195+
191196
replaceOriginalLinefeedInChangesWithSpaces = builder.replaceOriginalLinefeedInChangesWithSpaces;
192197

193198
Objects.requireNonNull(inlineDiffSplitter);
194199
Objects.requireNonNull(lineNormalizer);
195200
}
196201

197202
/**
198-
* Get the DiffRows describing the difference between original and revised texts using the given
199-
* patch. Useful for displaying side-by-side diff.
203+
* Get the DiffRows describing the difference between original and revised
204+
* texts using the given patch. Useful for displaying side-by-side diff.
200205
*
201206
* @param original the original text
202207
* @param revised the revised text
@@ -207,8 +212,9 @@ public List<DiffRow> generateDiffRows(List<String> original, List<String> revise
207212
}
208213

209214
/**
210-
* Generates the DiffRows describing the difference between original and revised texts using the
211-
* given patch. Useful for displaying side-by-side diff.
215+
* Generates the DiffRows describing the difference between original and
216+
* revised texts using the given patch. Useful for displaying side-by-side
217+
* diff.
212218
*
213219
* @param original the original text
214220
* @param patch the given patch
@@ -218,6 +224,9 @@ public List<DiffRow> generateDiffRows(final List<String> original, Patch<String>
218224
List<DiffRow> diffRows = new ArrayList<>();
219225
int endPos = 0;
220226
final List<AbstractDelta<String>> deltaList = patch.getDeltas();
227+
228+
decompressDeltas(deltaList);
229+
221230
for (AbstractDelta<String> delta : deltaList) {
222231
Chunk<String> orig = delta.getSource();
223232
Chunk<String> rev = delta.getTarget();
@@ -263,6 +272,44 @@ public List<DiffRow> generateDiffRows(final List<String> original, Patch<String>
263272
return diffRows;
264273
}
265274

275+
/**
276+
* Decompresses ChangeDeltas with different source and target size to a ChangeDelta with same size and
277+
* a following InsertDelta or DeleteDelta. With this problems of building DiffRows getting smaller.
278+
* @param deltaList
279+
*/
280+
private void decompressDeltas(final List<AbstractDelta<String>> deltaList) {
281+
for (int idx = 0; idx < deltaList.size(); idx++) {
282+
AbstractDelta<String> delta = deltaList.get(idx);
283+
if (delta.getType() == DeltaType.CHANGE && delta.getSource().size() != delta.getTarget().size()) {
284+
//System.out.println("decompress this " + delta);
285+
286+
List<AbstractDelta<String>> corrected = new ArrayList<>();
287+
int minSize = Math.min(delta.getSource().size(), delta.getTarget().size());
288+
Chunk<String> orig = delta.getSource();
289+
Chunk<String> rev = delta.getTarget();
290+
291+
deltaList.set(idx, new ChangeDelta<String>(
292+
new Chunk<>(orig.getPosition(), orig.getLines().subList(0, minSize)),
293+
new Chunk<>(rev.getPosition(), rev.getLines().subList(0, minSize))));
294+
295+
if (orig.getLines().size() < rev.getLines().size()) {
296+
deltaList.add(idx + 1, new InsertDelta<String>(
297+
new Chunk<>(orig.getPosition() + minSize, Collections.emptyList()),
298+
new Chunk<>(rev.getPosition() + minSize, rev.getLines().subList(minSize, rev.getLines().size()))));
299+
} else {
300+
deltaList.add(idx + 1, new DeleteDelta<String>(
301+
new Chunk<>(orig.getPosition() + minSize, orig.getLines().subList(minSize, orig.getLines().size())),
302+
new Chunk<>(rev.getPosition() + minSize, Collections.emptyList())));
303+
}
304+
305+
//System.out.println(" to " + corrected);
306+
}
307+
idx++;
308+
}
309+
310+
//System.out.println("got now " + deltaList);
311+
}
312+
266313
private DiffRow buildDiffRow(Tag type, String orgline, String newline) {
267314
if (reportLinesUnchanged) {
268315
return new DiffRow(type, orgline, newline);
@@ -436,8 +483,8 @@ public Builder ignoreWhiteSpaces(boolean val) {
436483
}
437484

438485
/**
439-
* Give the originial old and new text lines to Diffrow without any additional processing
440-
* and without any tags to highlight the change.
486+
* Give the originial old and new text lines to Diffrow without any
487+
* additional processing and without any tags to highlight the change.
441488
*
442489
* @param val the value to set. Default: false.
443490
* @return builder with configured reportLinesUnWrapped parameter
@@ -492,8 +539,8 @@ public Builder newTag(Function<Boolean, String> generator) {
492539
}
493540

494541
/**
495-
* Processor for diffed text parts. Here e.g. whitecharacters could be replaced by something
496-
* visible.
542+
* Processor for diffed text parts. Here e.g. whitecharacters could be
543+
* replaced by something visible.
497544
*
498545
* @param processDiffs
499546
* @return
@@ -504,10 +551,11 @@ public Builder processDiffs(Function<String, String> processDiffs) {
504551
}
505552

506553
/**
507-
* Set the column width of generated lines of original and revised texts.
554+
* Set the column width of generated lines of original and revised
555+
* texts.
508556
*
509-
* @param width the width to set. Making it < 0 doesn't make any sense. Default 80.
510-
* @return builder with config of column width
557+
* @param width the width to set. Making it < 0 doesn't make any sense.
558+
* Default 80. @return builder with config of column width
511559
*/
512560
public Builder columnWidth(int width) {
513561
if (width >= 0) {
@@ -517,7 +565,8 @@ public Builder columnWidth(int width) {
517565
}
518566

519567
/**
520-
* Build the DiffRowGenerator. If some parameters is not set, the default values are used.
568+
* Build the DiffRowGenerator. If some parameters is not set, the
569+
* default values are used.
521570
*
522571
* @return the customized DiffRowGenerator
523572
*/
@@ -526,8 +575,8 @@ public DiffRowGenerator build() {
526575
}
527576

528577
/**
529-
* Merge the complete result within the original text. This makes sense for one line
530-
* display.
578+
* Merge the complete result within the original text. This makes sense
579+
* for one line display.
531580
*
532581
* @param mergeOriginalRevised
533582
* @return
@@ -538,9 +587,9 @@ public Builder mergeOriginalRevised(boolean mergeOriginalRevised) {
538587
}
539588

540589
/**
541-
* Per default each character is separatly processed. This variant introduces processing by
542-
* word, which does not deliver in word changes. Therefore the whole word will be tagged as
543-
* changed:
590+
* Per default each character is separatly processed. This variant
591+
* introduces processing by word, which does not deliver in word
592+
* changes. Therefore the whole word will be tagged as changed:
544593
*
545594
* <pre>
546595
* false: (aBa : aba) -- changed: a(B)a : a(b)a
@@ -553,8 +602,9 @@ public Builder inlineDiffByWord(boolean inlineDiffByWord) {
553602
}
554603

555604
/**
556-
* To provide some customized splitting a splitter can be provided. Here someone could think
557-
* about sentence splitter, comma splitter or stuff like that.
605+
* To provide some customized splitting a splitter can be provided. Here
606+
* someone could think about sentence splitter, comma splitter or stuff
607+
* like that.
558608
*
559609
* @param inlineDiffSplitter
560610
* @return
@@ -565,9 +615,10 @@ public Builder inlineDiffBySplitter(Function<String, List<String>> inlineDiffSpl
565615
}
566616

567617
/**
568-
* By default DiffRowGenerator preprocesses lines for HTML output. Tabs and special HTML
569-
* characters like "&lt;" are replaced with its encoded value. To change this you can
570-
* provide a customized line normalizer here.
618+
* By default DiffRowGenerator preprocesses lines for HTML output. Tabs
619+
* and special HTML characters like "&lt;" are replaced with its encoded
620+
* value. To change this you can provide a customized line normalizer
621+
* here.
571622
*
572623
* @param lineNormalizer
573624
* @return
@@ -587,13 +638,14 @@ public Builder equalizer(BiPredicate<String, String> equalizer) {
587638
this.equalizer = equalizer;
588639
return this;
589640
}
590-
641+
591642
/**
592-
* Sometimes it happens that a change contains multiple lines. If there is no correspondence
593-
* in old and new. To keep the merged line more readable the linefeeds could be replaced
594-
* by spaces.
643+
* Sometimes it happens that a change contains multiple lines. If there
644+
* is no correspondence in old and new. To keep the merged line more
645+
* readable the linefeeds could be replaced by spaces.
646+
*
595647
* @param replace
596-
* @return
648+
* @return
597649
*/
598650
public Builder replaceOriginalLinefeedInChangesWithSpaces(boolean replace) {
599651
this.replaceOriginalLinefeedInChangesWithSpaces = replace;

java-diff-utils/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ public void testGeneratorWithMerge3() {
163163
assertEquals(6, rows.size());
164164
assertEquals("[CHANGE,<span class=\"editOldInline\">test</span>,anything]", rows.get(0).toString());
165165
assertEquals("[CHANGE,anything<span class=\"editOldInline\"> </span>,]", rows.get(1).toString());
166-
assertEquals("[CHANGE,<span class=\"editOldInline\"> </span>,]", rows.get(2).toString());
166+
assertEquals("[DELETE,<span class=\"editOldInline\"> </span>,]", rows.get(2).toString());
167167
assertEquals("[EQUAL,other,other]", rows.get(3).toString());
168168
assertEquals("[INSERT,<span class=\"editNewInline\">test</span>,test]", rows.get(4).toString());
169169
assertEquals("[INSERT,<span class=\"editNewInline\">test2</span>,test2]", rows.get(5).toString());
@@ -343,7 +343,7 @@ public void testGeneratorIssue22() {
343343
Arrays.asList(aa.split("\n")),
344344
Arrays.asList(bb.split("\n")));
345345

346-
assertEquals("[[CHANGE,This is a test ~senctence~.,This is a test **for diffutils**.], [CHANGE,,**This is the second line.**]]",
346+
assertEquals("[[CHANGE,This is a test ~senctence~.,This is a test **for diffutils**.], [INSERT,,**This is the second line.**]]",
347347
rows.toString());
348348

349349
System.out.println("|original|new|");
@@ -367,7 +367,7 @@ public void testGeneratorIssue22_2() {
367367
Arrays.asList(aa.split("\n")),
368368
Arrays.asList(bb.split("\n")));
369369

370-
assertEquals("[[CHANGE,This is a test ~for diffutils~.,This is a test **senctence**.], [CHANGE,~This is the second line.~,]]",
370+
assertEquals("[[CHANGE,This is a test ~for diffutils~.,This is a test **senctence**.], [DELETE,~This is the second line.~,]]",
371371
rows.toString());
372372
}
373373

@@ -385,7 +385,7 @@ public void testGeneratorIssue22_3() {
385385
Arrays.asList(aa.split("\n")),
386386
Arrays.asList(bb.split("\n")));
387387

388-
assertEquals("[[CHANGE,This is a test ~senctence~.,This is a test **for diffutils**.], [CHANGE,,**This is the second line.**], [CHANGE,,**And one more.**]]",
388+
assertEquals("[[CHANGE,This is a test ~senctence~.,This is a test **for diffutils**.], [INSERT,,**This is the second line.**], [INSERT,,**And one more.**]]",
389389
rows.toString());
390390
}
391391

@@ -627,6 +627,26 @@ public void testCorrectChangeIssue114() throws IOException {
627627
List<String> original = Arrays.asList("A", "B", "C", "D", "E");
628628
List<String> revised = Arrays.asList("a", "C", "", "E");
629629

630+
DiffRowGenerator generator = DiffRowGenerator.create()
631+
.showInlineDiffs(false)
632+
.inlineDiffByWord(true)
633+
.oldTag(f -> "~")
634+
.newTag(f -> "**")
635+
.build();
636+
List<DiffRow> rows = generator.generateDiffRows(original, revised);
637+
638+
for (DiffRow diff : rows) {
639+
System.out.println(diff);
640+
}
641+
642+
assertThat(rows).extracting(item -> item.getTag().name()).containsExactly("CHANGE", "DELETE", "EQUAL", "CHANGE", "EQUAL");
643+
}
644+
645+
@Test
646+
public void testCorrectChangeIssue114_2() throws IOException {
647+
List<String> original = Arrays.asList("A", "B", "C", "D", "E");
648+
List<String> revised = Arrays.asList("a", "C", "", "E");
649+
630650
DiffRowGenerator generator = DiffRowGenerator.create()
631651
.showInlineDiffs(true)
632652
.inlineDiffByWord(true)
@@ -638,5 +658,8 @@ public void testCorrectChangeIssue114() throws IOException {
638658
for (DiffRow diff : rows) {
639659
System.out.println(diff);
640660
}
661+
662+
assertThat(rows).extracting(item -> item.getTag().name()).containsExactly("CHANGE", "DELETE", "EQUAL", "CHANGE", "EQUAL");
663+
assertThat(rows.get(1).toString()).isEqualTo("[DELETE,~B~,]");
641664
}
642665
}

0 commit comments

Comments
 (0)