1717
1818import com .github .difflib .DiffUtils ;
1919import com .github .difflib .patch .AbstractDelta ;
20+ import com .github .difflib .patch .ChangeDelta ;
2021import com .github .difflib .patch .Chunk ;
22+ import com .github .difflib .patch .DeleteDelta ;
2123import com .github .difflib .patch .DeltaType ;
24+ import com .github .difflib .patch .InsertDelta ;
2225import com .github .difflib .patch .Patch ;
2326import com .github .difflib .text .DiffRow .Tag ;
2427import java .util .*;
3033import 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) {
187192reportLinesUnchanged =builder .reportLinesUnchanged ;
188193lineNormalizer =builder .lineNormalizer ;
189194processDiffs =builder .processDiffs ;
190-
195+
191196replaceOriginalLinefeedInChangesWithSpaces =builder .replaceOriginalLinefeedInChangesWithSpaces ;
192197
193198Objects .requireNonNull (inlineDiffSplitter );
194199Objects .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>
218224List <DiffRow >diffRows =new ArrayList <>();
219225int endPos =0 ;
220226final List <AbstractDelta <String >>deltaList =patch .getDeltas ();
227+
228+ decompressDeltas (deltaList );
229+
221230for (AbstractDelta <String >delta :deltaList ) {
222231Chunk <String >orig =delta .getSource ();
223232Chunk <String >rev =delta .getTarget ();
@@ -263,6 +272,44 @@ public List<DiffRow> generateDiffRows(final List<String> original, Patch<String>
263272return 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+
266313private DiffRow buildDiffRow (Tag type ,String orgline ,String newline ) {
267314if (reportLinesUnchanged ) {
268315return 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 */
512560public Builder columnWidth (int width ) {
513561if (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 "<" 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 "<" 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) {
587638this .equalizer =equalizer ;
588639return 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 */
598650public Builder replaceOriginalLinefeedInChangesWithSpaces (boolean replace ) {
599651this .replaceOriginalLinefeedInChangesWithSpaces =replace ;