3030import difflib .patch .Patch ;
3131import difflib .text .DiffRow .Tag ;
3232import java .util .*;
33+ import java .util .regex .Matcher ;
34+ import java .util .regex .Pattern ;
3335
3436/**
3537 * This class for generating DiffRows for side-by-sidy view. You can customize the way of
@@ -54,9 +56,10 @@ public class DiffRowGenerator {
5456 private final String inlineNewTag ;
5557 private final String inlineOldCssClass ;
5658 private final String inlineNewCssClass ;
59+ private final boolean inlineDiffByWord ;
5760 private final int columnWidth ;
5861 private final Equalizer <String > equalizer ;
59- private final boolean mergeInline ;
62+ private final boolean mergeOriginalRevised ;
6063
6164 /**
6265 * This class used for building the DiffRowGenerator.
@@ -73,7 +76,8 @@ public static class Builder {
7376 private String inlineOldCssClass = "editOldInline" ;
7477 private String inlineNewCssClass = "editNewInline" ;
7578 private int columnWidth = 80 ;
76- private boolean mergeInline = false ;
79+ private boolean mergeOriginalRevised = false ;
80+ private boolean inlineDiffByWord = false ;
7781
7882 private Builder () {
7983 }
@@ -166,11 +170,25 @@ public DiffRowGenerator build() {
166170 return new DiffRowGenerator (this );
167171 }
168172
169- public Builder mergeInline (boolean mergeInline ) {
170- this .mergeInline = mergeInline ;
173+ /**
174+ * Merge the complete result within the original text. This makes sense for one line
175+ * display.
176+ * @param mergeOriginalRevised
177+ * @return
178+ */
179+ public Builder mergeOriginalRevised (boolean mergeOriginalRevised ) {
180+ this .mergeOriginalRevised = mergeOriginalRevised ;
181+ return this ;
182+ }
183+
184+ /**
185+ * Per default each character is separatly processed. This variant introduces processing
186+ * by word, which should deliver no in word changes.
187+ */
188+ public Builder inlineDiffByWord (boolean inlineDiffByWord ) {
189+ this .inlineDiffByWord = inlineDiffByWord ;
171190 return this ;
172191 }
173-
174192 }
175193
176194 public static Builder create () {
@@ -185,7 +203,8 @@ private DiffRowGenerator(Builder builder) {
185203 inlineOldCssClass = builder .inlineOldCssClass ;
186204 inlineNewCssClass = builder .inlineNewCssClass ;
187205 columnWidth = builder .columnWidth ;
188- mergeInline = builder .mergeInline ;
206+ mergeOriginalRevised = builder .mergeOriginalRevised ;
207+ inlineDiffByWord = builder .inlineDiffByWord ;
189208 equalizer = new Equalizer <String >() {
190209 @ Override
191210 public boolean equals (String original , String revised ) {
@@ -212,11 +231,11 @@ public List<DiffRow> generateDiffRows(List<String> original, List<String> revise
212231
213232 private DiffRow buildDiffRow (Tag type , String orgline , String newline ) {
214233 String wrapOrg = StringUtils .wrapText (StringUtils .normalize (orgline ), columnWidth );
215- if (showInlineDiffs && Tag .DELETE == type ) {
234+ if (mergeOriginalRevised && Tag .DELETE == type ) {
216235 wrapOrg = createOpenTag (inlineOldTag , inlineOldCssClass ) + wrapOrg + createCloseTag (inlineOldTag );
217236 }
218237 String wrapNew = StringUtils .wrapText (StringUtils .normalize (newline ), columnWidth );
219- if (showInlineDiffs && Tag .INSERT == type ) {
238+ if (mergeOriginalRevised && Tag .INSERT == type ) {
220239 wrapOrg = createOpenTag (inlineNewTag , inlineNewCssClass ) + wrapNew + createCloseTag (inlineNewTag );
221240 }
222241 return new DiffRow (type , wrapOrg , wrapNew );
@@ -295,14 +314,23 @@ public List<DiffRow> generateDiffRows(final List<String> original, Patch<String>
295314 private List <DiffRow > generateInlineDiffs (Delta <String > delta ) throws DiffException {
296315 List <String > orig = StringUtils .normalize (delta .getOriginal ().getLines ());
297316 List <String > rev = StringUtils .normalize (delta .getRevised ().getLines ());
298- LinkedList <String > origList = new LinkedList <>();
299- for (Character character : String .join ("\n " , orig ).toCharArray ()) {
300- origList .add (character .toString ());
301- }
302- LinkedList <String > revList = new LinkedList <>();
303- for (Character character : String .join ("\n " , rev ).toCharArray ()) {
304- revList .add (character .toString ());
317+ List <String > origList ;
318+ List <String > revList ;
319+
320+ if (inlineDiffByWord ) {
321+ origList = splitStringPreserveDelimiter (String .join ("\n " , orig ));
322+ revList = splitStringPreserveDelimiter (String .join ("\n " , rev ));
323+ } else {
324+ origList = new LinkedList <>();
325+ revList = new LinkedList <>();
326+ for (Character character : String .join ("\n " , orig ).toCharArray ()) {
327+ origList .add (character .toString ());
328+ }
329+ for (Character character : String .join ("\n " , rev ).toCharArray ()) {
330+ revList .add (character .toString ());
331+ }
305332 }
333+
306334 List <Delta <String >> inlineDeltas = DiffUtils .diff (origList , revList ).getDeltas ();
307335
308336 Collections .reverse (inlineDeltas );
@@ -314,7 +342,7 @@ private List<DiffRow> generateInlineDiffs(Delta<String> delta) throws DiffExcept
314342 .getPosition ()
315343 + inlineOrig .size () + 1 , this .inlineOldTag , this .inlineOldCssClass );
316344 } else if (inlineDelta instanceof InsertDelta ) {
317- if (mergeInline ) {
345+ if (mergeOriginalRevised ) {
318346 origList .addAll (inlineOrig .getPosition (),
319347 revList .subList (inlineRev .getPosition (), inlineRev .getPosition ()
320348 + inlineRev .size ()));
@@ -325,11 +353,11 @@ private List<DiffRow> generateInlineDiffs(Delta<String> delta) throws DiffExcept
325353 + inlineRev .size () + 1 , this .inlineNewTag , this .inlineNewCssClass );
326354 }
327355 } else if (inlineDelta instanceof ChangeDelta ) {
328- if (mergeInline ) {
329- origList .addAll (inlineOrig .getPosition (),
356+ if (mergeOriginalRevised ) {
357+ origList .addAll (inlineOrig .getPosition () + inlineOrig . size () ,
330358 revList .subList (inlineRev .getPosition (), inlineRev .getPosition ()
331359 + inlineRev .size ()));
332- wrapInTag (origList , inlineOrig .getPosition (), inlineOrig .getPosition ()
360+ wrapInTag (origList , inlineOrig .getPosition () + inlineOrig . size () , inlineOrig .getPosition () + inlineOrig . size ()
333361 + inlineRev .size () + 1 , this .inlineNewTag , this .inlineNewCssClass );
334362 } else {
335363 wrapInTag (revList , inlineRev .getPosition (), inlineRev .getPosition ()
@@ -370,7 +398,7 @@ private List<DiffRow> generateInlineDiffs(Delta<String> delta) throws DiffExcept
370398 * @param tag the tag name without angle brackets, just a word
371399 * @param cssClass the optional css class
372400 */
373- public static void wrapInTag (LinkedList <String > sequence , int startPosition ,
401+ public static void wrapInTag (List <String > sequence , int startPosition ,
374402 int endPosition , String tag , String cssClass ) {
375403 sequence .add (startPosition , createOpenTag (tag , cssClass ));
376404 sequence .add (endPosition , createCloseTag (tag ));
@@ -383,4 +411,21 @@ private static String createCloseTag(String tag) {
383411 private static String createOpenTag (String tag , String cssClass ) {
384412 return "<" + tag + (cssClass != null ? " class=\" " + cssClass + "\" " : "" ) + ">" ;
385413 }
414+
415+ private static final Pattern SPLIT_PATTERN = Pattern .compile ("\\ s+" );
416+
417+ private List <String > splitStringPreserveDelimiter (String str ) {
418+ List <String > list = new ArrayList <>();
419+ if (str != null ) {
420+ Matcher matcher = SPLIT_PATTERN .matcher (str );
421+ int pos = 0 ;
422+ while (matcher .find ()) {
423+ list .add (str .substring (pos , matcher .start ()));
424+ list .add (matcher .group ());
425+ pos = matcher .end ();
426+ }
427+ list .add (str .substring (pos ));
428+ }
429+ return list ;
430+ }
386431}
0 commit comments