diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000000000000000000000000000000000000..041b5c5122fd94748f90ce4cc3bcacee02586fd2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,24 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Example data
+2. simple programm snippet
+3. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**System**
+ - Java version
+ - Version [e.g. 22]
diff --git a/.gitignore b/.gitignore
index 0643ce7d4db53b5f89aa0ff88388a405cf0d6cdc..54a9c8cab252ea43b5a67dcb5e1dd3248bfa6887 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
-/target/
-/nbproject/
\ No newline at end of file
+.idea/
+build/
+nbproject/
+target/
diff --git a/.travis.yml b/.travis.yml
index e68631bc3763b30c0c0793836b447eb8a6d847c6..a4870debefd821883960deaef432fdf9111a1d14 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,7 @@
 language: java
 jdk:
-  - oraclejdk8
+  - openjdk8
+  - openjdk11
   
 after_success:
-  - mvn clean
\ No newline at end of file
+  - mvn clean
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..2ba1be15dbb35f50d70b65114c6b6b831247be0f
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,85 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+This project uses a custom versioning scheme (and not [Semantic Versioning](https://semver.org/spec/v2.0.0.html)).
+
+## [Unreleased]
+
+### Changed
+
+* Exchange `0 += 1` for `0 = 1` in UnifiedDiffUtils
+* preview of new Unified Diff Reader / Writer. This is not yet feature complete but passes the tests of the old version.
+  * feel free to issue some change requests for the api.
+* introduces lineNormalizer extension point to e.g. change html code encoding. (issue #41)
+
+## [4.0] – 2019-01-09
+
+### Changed
+
+* moved to organisation **java-diff-utils**
+* changed groupid to **io.github.java-diff-utils** and artifact id to **java-diff-utils**
+
+## [3.0] – 2018-10-18
+
+### Added
+
+* Introduced a process listener to diff algorithms. For long running
+  diffs one could implement some progress information.
+* automatic module name for JDK 9 and higher usage
+
+### Changed
+
+* changed generation of inline diffes, if there are different linefeeds within one diff, then these are excluded from the diff block.
+
+### Removed
+
+* Due to licensing issues Delta.java and DiffAlgorithm.java were removed.
+
+## [2.2] – 2017-11-09
+
+### Added
+
+* released at maven central
+* included checkstyle source code conventions
+* allow configurable splitting of lines to define the blocks to compare (words, characters, phrases).
+
+### Changed
+
+* groupid changed to **com.github.wumpz**, due to maven central releasing
+
+## [2.0] – 2017-08-14
+
+### Added
+
+* support for inline merge
+* integrated JGit (Eclipse Licensed) to provide HistogramDiff to gain speed for large datasets
+
+### Changed
+
+* switch to maven and removed other artifacts
+* changed groupid to **com.github.java-diff-utils** due to different forks at github
+* updated maven plugins
+* JDK 1.8 compatibility, sorry if you have to stick with older versions
+* restructured packages heavily
+* changed API
+* changed Algorithm to provide only cursor positions
+
+### Removed
+
+* removed all kinds of helper classes in favour of new JDK 8 function classes like Predicate
+
+## 1.2
+
+### Added
+
+* JDK 1.5 compatibility
+* Ant build script
+* Generate output in unified diff format (thanks for Bill James)
+
+[Unreleased]: https://github.com/java-diff-utils/java-diff-utils/compare/java-diff-utils-4.0...HEAD
+[4.0]: https://github.com/java-diff-utils/java-diff-utils/compare/diff-utils-3.0...java-diff-utils-4.0
+[3.0]: https://github.com/java-diff-utils/java-diff-utils/compare/diff-utils-2.2...diff-utils-3.0
+[2.2]: https://github.com/java-diff-utils/java-diff-utils/compare/diff-utils-2.0...diff-utils-2.2
+
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
deleted file mode 100644
index 057f67a40ad4fd9b0e557ccf4df6b0ab656348af..0000000000000000000000000000000000000000
--- a/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,17 +0,0 @@
-### Expected Behavior
-
-
-### Actual Behavior
-
-
-### Steps to Reproduce the Problem
-
-  1.
-  1.
-  1.
-
-### Specifications
-
-  - Version:
-  - Platform:
-  - Subsystem:
diff --git a/README.md b/README.md
index e62f0e5ccb2447c545a6cacaec13cf41db70f3ef..e0d8f1226bd962d398f0064e5c384b2d73e94b7e 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,22 @@
 # java-diff-utils
 
-## Status ##
-[![Build Status](https://travis-ci.org/wumpz/java-diff-utils.svg?branch=master)](https://travis-ci.org/java-diff-utils/java-diff-utils)     [![Codacy Badge](https://api.codacy.com/project/badge/Grade/7eba77f10bed4c2a8d08ac8dc8da4a86)](https://www.codacy.com/app/wumpz/java-diff-utils?utm_source=github.com&utm_medium=referral&utm_content=java-diff-utils/java-diff-utils&utm_campaign=Badge_Grade)
-[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.wumpz/diffutils/badge.svg)](http://maven-badges.herokuapp.com/maven-central/com.github.wumpz/diffutils)
+## Status
 
+[![Build Status](https://travis-ci.org/java-diff-utils/java-diff-utils.svg?branch=master)](https://travis-ci.org/java-diff-utils/java-diff-utils)
+[![Codacy Badge](https://api.codacy.com/project/badge/Grade/7eba77f10bed4c2a8d08ac8dc8da4a86)](https://www.codacy.com/app/wumpz/java-diff-utils?utm_source=github.com&utm_medium=referral&utm_content=java-diff-utils/java-diff-utils&utm_campaign=Badge_Grade)
+[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.github.java-diff-utils/java-diff-utils/badge.svg)](http://maven-badges.herokuapp.com/maven-central/io.github.java-diff-utils/java-diff-utils)
+
+## Intro
 
-## Intro ##
 Diff Utils library is an OpenSource library for performing the comparison operations between texts: computing diffs, applying patches, generating unified diffs or parsing them, generating diff output for easy future displaying (like side-by-side view) and so on.
 
 Main reason to build this library was the lack of easy-to-use libraries with all the usual stuff you need while working with diff files. Originally it was inspired by JRCS library and it's nice design of diff module.
 
 **This is originally a fork of java-diff-utils from Google Code Archive.**
 
-## Examples ##
+## Examples
 
-Look [here](https://github.com/wumpz/java-diff-utils/wiki) to find more helpful informations and examples. 
+Look [here](https://github.com/wumpz/java-diff-utils/wiki) to find more helpful informations and examples.
 
 These two outputs are generated using this java-diff-utils. The source code can also be found at the *Examples* page:
 
@@ -22,7 +24,6 @@ These two outputs are generated using this java-diff-utils. The source code can
 
 This is a test ~senctence~**for diffutils**.
 
-
 **Producing a side by side view of computed differences.**
 
 |original|new|
@@ -31,102 +32,61 @@ This is a test ~senctence~**for diffutils**.
 |This is the second line.|This is the second line.|
 |~And here is the finish.~||
 
+## Main Features
 
-## Main Features ##
+* computing the difference between two texts.
+* capable to hand more than plain ascii. Arrays or List of any type that implements hashCode() and equals() correctly can be subject to differencing using this library
+* patch and unpatch the text with the given patch
+* parsing the unified diff format
+* producing human-readable differences
+* inline difference construction
+* Algorithms:
+  * Myer
+  * HistogramDiff using JGit Library
 
-  * computing the difference between two texts.
-  * capable to hand more than plain ascci. Arrays or List of any type that implements hashCode() and equals() correctly can be subject to differencing using this library
-  * patch and unpatch the text with the given patch
-  * parsing the unified diff format
-  * producing human-readable differences
-  * inline difference construction
-  * Algorithms:
-    * Myer
-    * HistogramDiff using JGit Library
-
-### Algoritms ###
+### Algorithms
 
 * Myer's diff
-* HistogramDiff 
+* HistogramDiff
 
 But it can easily replaced by any other which is better for handing your texts. I have plan to add implementation of some in future.
 
-### Changelog ###
-  * Version 4.0-SNAPSHOT
-    * moved to organisation **java-diff-utils**
-    * changed groupid to **io.github.java-diff-utils** and artifact id to **java-diff-utils**
-  * Version 3.0
-    * changed generation of inline diffes, if there are different linefeeds within one diff, then these are excluded 
-      from the diff block. 
-    * Due to licensing issues Delta.java and DiffAlgorithm.java were removed.
-  * Version 2.3-SNAPSHOT
-    * Introduced a process listener to diff algorithms. For long running
-      diffs one could implement some progress information.
-    * automatic module name for JDK 9 and higher usage
-  * Version 2.2
-    * released at maven central
-    * included checkstyle source code conventions
-    * groupid changed to **com.github.wumpz**, due to maven central releasing
-    * allow configurable splitting of lines to define the blocks to compare (words, characters, phrases).
-  * Version 2.0
-    * switch to maven and removed other artifacts
-    * changed groupid to **com.github.java-diff-utils** due to different forks at github
-    * updated maven plugins
-    * JDK 1.8 compatibility, sorry if you have to stick with older versions
-    * support for inline merge
-    * restructured packages heavily
-    * changed API 
-    * changed Algorithm to provide only cursor positions
-    * integrated JGit (Eclipse Licensed) to provide HistogramDiff to gain speed for large datasets 
-    * removed all kinds of helper classes in favour of new JDK 8 function classes like Predicate
-  * Version 1.2
-    * JDK 1.5 compatibility
-    * Ant build script
-    * Generate output in unified diff format (thanks for Bill James)
-
 ## Source Code conventions
 
 Recently a checkstyle process was integrated into the build process. java-diff-utils follows the sun java format convention. There are no TABs allowed. Use spaces.
 
 ```java
 public static <T> Patch<T> diff(List<T> original, List<T> revised,
-	BiPredicate<T, T> equalizer) throws DiffException {
-	if (equalizer != null) {
-		return DiffUtils.diff(original, revised,
-				new MyersDiff<>(equalizer));
-	}
-	return DiffUtils.diff(original, revised, new MyersDiff<>());
+    BiPredicate<T, T> equalizer) throws DiffException {
+    if (equalizer != null) {
+        return DiffUtils.diff(original, revised,
+        new MyersDiff<>(equalizer));
+    }
+    return DiffUtils.diff(original, revised, new MyersDiff<>());
 }
 ```
 
 This is a valid piece of source code:
+
 * blocks without braces are not allowed
 * after control statements (if, while, for) a whitespace is expected
 * the opening brace should be in the same line as the control statement
 
-### To Install ###
+### To Install
 
 Just add the code below to your maven dependencies:
-```
-<dependency>
-    <groupId>com.github.wumpz</groupId>
-    <artifactId>diffutils</artifactId>
-    <version>3.0</version>
-</dependency>
-```
-
-Attention. We changed groupid and artifactid. Starting with version 4 you have to use:
 
-```
+```xml
 <dependency>
     <groupId>io.github.java-diff-utils</groupId>
     <artifactId>java-diff-utils</artifactId>
-    <version>4.0-SNAPSHOT</version>
+    <version>4.0</version>
 </dependency>
 ```
 
 or using gradle:
-```
-// https://mvnrepository.com/artifact/com.github.wumpz/diffutils
-compile group: 'com.github.wumpz', name: 'diffutils', version: '2.2'
+
+```groovy
+// https://mvnrepository.com/artifact/io.github.java-diff-utils/java-diff-utils
+implementation "io.github.java-diff-utils:java-diff-utils:4.0"
 ```
diff --git a/java-diff-utils-jgit/nb-configuration.xml b/java-diff-utils-jgit/nb-configuration.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ca588fa8dbba2a7a1566772665f8c70c90a6ba87
--- /dev/null
+++ b/java-diff-utils-jgit/nb-configuration.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-shared-configuration>
+    <!--
+This file contains additional configuration written by modules in the NetBeans IDE.
+The configuration is intended to be shared among all the users of project and
+therefore it is assumed to be part of version control checkout.
+Without this configuration present, some functionality in the IDE may be limited or fail altogether.
+-->
+    <properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
+        <!--
+Properties that influence various parts of the IDE, especially code formatting and the like. 
+You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
+That way multiple projects can share the same settings (useful for formatting rules for example).
+Any value defined here will override the pom.xml file value but is only applicable to the current project.
+-->
+        <netbeans.compile.on.save>none</netbeans.compile.on.save>
+        <netbeans.hint.jdkPlatform>JDK_1.8</netbeans.hint.jdkPlatform>
+    </properties>
+</project-shared-configuration>
diff --git a/java-diff-utils-jgit/pom.xml b/java-diff-utils-jgit/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7b37a55080aa7dd7ab1650dc6b03a5ea246e4e07
--- /dev/null
+++ b/java-diff-utils-jgit/pom.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>io.github.java-diff-utils</groupId>
+        <artifactId>java-diff-utils-parent</artifactId>
+        <version>4.4</version>
+    </parent>
+    <artifactId>java-diff-utils-jgit</artifactId>
+    <name>java-diff-utils-jgit</name>
+    <packaging>jar</packaging>
+    <description>This is an extension of java-diff-utils using jgit to use its implementation of
+some difference algorithms.</description>
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <type>jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jgit</groupId>
+            <artifactId>org.eclipse.jgit</artifactId>
+            <version>4.4.1.201607150455-r</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.googlecode.javaewah</groupId>
+                    <artifactId>JavaEWAH</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-codec</groupId>
+                    <artifactId>commons-codec</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.httpcomponents</groupId>
+                    <artifactId>httpclient</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.jcraft</groupId>
+                    <artifactId>jsch</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>java-diff-utils</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/src/main/java/com/github/difflib/algorithm/jgit/HistogramDiff.java b/java-diff-utils-jgit/src/main/java/com/github/difflib/algorithm/jgit/HistogramDiff.java
similarity index 96%
rename from src/main/java/com/github/difflib/algorithm/jgit/HistogramDiff.java
rename to java-diff-utils-jgit/src/main/java/com/github/difflib/algorithm/jgit/HistogramDiff.java
index c2e941f2f3556a0d83fe5bb0dabd9c3ce48d3570..e3f6cd5b5fad7d357ed18c78989a2b86cb8402ef 100644
--- a/src/main/java/com/github/difflib/algorithm/jgit/HistogramDiff.java
+++ b/java-diff-utils-jgit/src/main/java/com/github/difflib/algorithm/jgit/HistogramDiff.java
@@ -18,7 +18,6 @@ package com.github.difflib.algorithm.jgit;
 import com.github.difflib.algorithm.Change;
 import com.github.difflib.algorithm.DiffAlgorithmI;
 import com.github.difflib.algorithm.DiffAlgorithmListener;
-import com.github.difflib.algorithm.DiffException;
 import com.github.difflib.patch.DeltaType;
 import java.util.ArrayList;
 import java.util.List;
@@ -37,7 +36,7 @@ import org.eclipse.jgit.diff.SequenceComparator;
 public class HistogramDiff<T> implements DiffAlgorithmI<T> {
 
     @Override
-    public List<Change> computeDiff(List<T> source, List<T> target, DiffAlgorithmListener progress) throws DiffException {
+    public List<Change> computeDiff(List<T> source, List<T> target, DiffAlgorithmListener progress) {
         Objects.requireNonNull(source, "source list must not be null");
         Objects.requireNonNull(target, "target list must not be null");
         if (progress != null) {
diff --git a/src/test/java/com/github/difflib/algorithm/jgit/HistogramDiffTest.java b/java-diff-utils-jgit/src/test/java/com/github/difflib/algorithm/jgit/HistogramDiffTest.java
similarity index 94%
rename from src/test/java/com/github/difflib/algorithm/jgit/HistogramDiffTest.java
rename to java-diff-utils-jgit/src/test/java/com/github/difflib/algorithm/jgit/HistogramDiffTest.java
index 0009f18b20a5ca0e95b88546f6b24ab2627beb5f..e1030242a5969140c6d0bfff795829d9b97fb082 100644
--- a/src/test/java/com/github/difflib/algorithm/jgit/HistogramDiffTest.java
+++ b/java-diff-utils-jgit/src/test/java/com/github/difflib/algorithm/jgit/HistogramDiffTest.java
@@ -16,7 +16,6 @@
 package com.github.difflib.algorithm.jgit;
 
 import com.github.difflib.algorithm.DiffAlgorithmListener;
-import com.github.difflib.algorithm.DiffException;
 import com.github.difflib.patch.Patch;
 import com.github.difflib.patch.PatchFailedException;
 import java.util.ArrayList;
@@ -58,7 +57,7 @@ public class HistogramDiffTest {
      * Test of diff method, of class HistogramDiff.
      */
     @Test
-    public void testDiff() throws DiffException, PatchFailedException {
+    public void testDiff() throws PatchFailedException {
         List<String> orgList = Arrays.asList("A", "B", "C", "A", "B", "B", "A");
         List<String> revList = Arrays.asList("C", "B", "A", "B", "A", "C");
         final Patch<String> patch = Patch.generate(orgList, revList, new HistogramDiff().computeDiff(orgList, revList, null));
@@ -72,7 +71,7 @@ public class HistogramDiffTest {
     }
     
     @Test
-    public void testDiffWithListener() throws DiffException, PatchFailedException {
+    public void testDiffWithListener() throws PatchFailedException {
         List<String> orgList = Arrays.asList("A", "B", "C", "A", "B", "B", "A");
         List<String> revList = Arrays.asList("C", "B", "A", "B", "A", "C");
         
diff --git a/src/test/java/com/github/difflib/algorithm/jgit/LRHistogramDiffTest.java b/java-diff-utils-jgit/src/test/java/com/github/difflib/algorithm/jgit/LRHistogramDiffTest.java
similarity index 77%
rename from src/test/java/com/github/difflib/algorithm/jgit/LRHistogramDiffTest.java
rename to java-diff-utils-jgit/src/test/java/com/github/difflib/algorithm/jgit/LRHistogramDiffTest.java
index 5f93570d108ab3812b79758f7a70732413813591..02dfe85d141b5853a6325412dd0ae2c2c7709755 100644
--- a/src/test/java/com/github/difflib/algorithm/jgit/LRHistogramDiffTest.java
+++ b/java-diff-utils-jgit/src/test/java/com/github/difflib/algorithm/jgit/LRHistogramDiffTest.java
@@ -15,15 +15,18 @@
  */
 package com.github.difflib.algorithm.jgit;
 
-import static com.github.difflib.DiffUtilsTest.readStringListFromInputStream;
-import com.github.difflib.TestConstants;
 import com.github.difflib.algorithm.DiffAlgorithmListener;
-import com.github.difflib.algorithm.DiffException;
 import com.github.difflib.patch.Patch;
 import com.github.difflib.patch.PatchFailedException;
+import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
+import static java.util.stream.Collectors.toList;
 import java.util.zip.ZipFile;
 import org.junit.After;
 import org.junit.AfterClass;
@@ -58,8 +61,8 @@ public class LRHistogramDiffTest {
     }
 
     @Test
-    public void testPossibleDiffHangOnLargeDatasetDnaumenkoIssue26() throws IOException, DiffException, PatchFailedException {
-        ZipFile zip = new ZipFile(TestConstants.MOCK_FOLDER + "/large_dataset1.zip");
+    public void testPossibleDiffHangOnLargeDatasetDnaumenkoIssue26() throws IOException, PatchFailedException {
+        ZipFile zip = new ZipFile("target/test-classes/mocks/large_dataset1.zip");
         List<String> original = readStringListFromInputStream(zip.getInputStream(zip.getEntry("ta")));
         List<String> revised = readStringListFromInputStream(zip.getInputStream(zip.getEntry("tb")));
 
@@ -86,7 +89,14 @@ public class LRHistogramDiffTest {
         List<String> created = patch.applyTo(original);
         assertArrayEquals(revised.toArray(), created.toArray());
         
-        assertEquals(50, logdata.size());
+        assertEquals(246579, logdata.size());
     }
 
+    public static List<String> readStringListFromInputStream(InputStream is) throws IOException {
+        try (BufferedReader reader = new BufferedReader(
+                new InputStreamReader(is, Charset.forName(StandardCharsets.UTF_8.name())))) {
+
+            return reader.lines().collect(toList());
+        }
+    }
 }
diff --git a/src/test/resources/mocks/large_dataset1.zip b/java-diff-utils-jgit/src/test/resources/mocks/large_dataset1.zip
similarity index 100%
rename from src/test/resources/mocks/large_dataset1.zip
rename to java-diff-utils-jgit/src/test/resources/mocks/large_dataset1.zip
diff --git a/java-diff-utils/pom.xml b/java-diff-utils/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ea4d774a257eb9dba7500e27b93563811c65aef6
--- /dev/null
+++ b/java-diff-utils/pom.xml
@@ -0,0 +1,60 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>io.github.java-diff-utils</groupId>
+    <artifactId>java-diff-utils</artifactId>
+    <packaging>jar</packaging>
+    <name>java-diff-utils</name>
+    <parent>
+        <groupId>io.github.java-diff-utils</groupId>
+        <artifactId>java-diff-utils-parent</artifactId>
+        <version>4.4</version>
+    </parent>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <type>jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>3.11.1</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>	
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.6.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>3.0.2</version>
+                <configuration>
+                    <archive>
+                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                        <manifestEntries>
+                            <!-- identical to OSGI name -->
+                            <Automatic-Module-Name>io.github.java-diff-utils</Automatic-Module-Name>
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
+
diff --git a/src/main/java/com/github/difflib/DiffUtils.java b/java-diff-utils/src/main/java/com/github/difflib/DiffUtils.java
similarity index 98%
rename from src/main/java/com/github/difflib/DiffUtils.java
rename to java-diff-utils/src/main/java/com/github/difflib/DiffUtils.java
index 8b144642173334be865a91628a14e86e64e10883..b57a0d2f2e1db628e149e43d5addc88069e9a413 100644
--- a/src/main/java/com/github/difflib/DiffUtils.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/DiffUtils.java
@@ -28,7 +28,6 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.BiPredicate;
-import static java.util.stream.Collectors.joining;
 
 /**
  * Implements the difference and patching engine
@@ -143,7 +142,7 @@ public final class DiffUtils {
         if (lines.isEmpty()) {
             return Collections.emptyList();
         }
-        return Collections.singletonList(lines.stream().collect(joining(delimiter)));
+        return Collections.singletonList(String.join(delimiter, lines));
     }
 
     /**
diff --git a/src/main/java/com/github/difflib/UnifiedDiffUtils.java b/java-diff-utils/src/main/java/com/github/difflib/UnifiedDiffUtils.java
similarity index 99%
rename from src/main/java/com/github/difflib/UnifiedDiffUtils.java
rename to java-diff-utils/src/main/java/com/github/difflib/UnifiedDiffUtils.java
index fe5b6f69631afb40524e8b38aaaccddbf963ccf3..d5812c3193c587ee103f9565cb2aa9ab15e326bb 100644
--- a/src/main/java/com/github/difflib/UnifiedDiffUtils.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/UnifiedDiffUtils.java
@@ -83,10 +83,10 @@ public final class UnifiedDiffUtils {
                 new_ln = m.group(3) == null ? 1 : Integer.parseInt(m.group(3));
 
                 if (old_ln == 0) {
-                    old_ln += 1;
+                    old_ln = 1;
                 }
                 if (new_ln == 0) {
-                    new_ln += 1;
+                    new_ln = 1;
                 }
             } else {
                 if (line.length() > 0) {
diff --git a/src/main/java/com/github/difflib/algorithm/Change.java b/java-diff-utils/src/main/java/com/github/difflib/algorithm/Change.java
similarity index 100%
rename from src/main/java/com/github/difflib/algorithm/Change.java
rename to java-diff-utils/src/main/java/com/github/difflib/algorithm/Change.java
diff --git a/src/main/java/com/github/difflib/algorithm/DiffAlgorithmI.java b/java-diff-utils/src/main/java/com/github/difflib/algorithm/DiffAlgorithmI.java
similarity index 100%
rename from src/main/java/com/github/difflib/algorithm/DiffAlgorithmI.java
rename to java-diff-utils/src/main/java/com/github/difflib/algorithm/DiffAlgorithmI.java
diff --git a/src/main/java/com/github/difflib/algorithm/DiffAlgorithmListener.java b/java-diff-utils/src/main/java/com/github/difflib/algorithm/DiffAlgorithmListener.java
similarity index 100%
rename from src/main/java/com/github/difflib/algorithm/DiffAlgorithmListener.java
rename to java-diff-utils/src/main/java/com/github/difflib/algorithm/DiffAlgorithmListener.java
diff --git a/src/main/java/com/github/difflib/algorithm/DiffException.java b/java-diff-utils/src/main/java/com/github/difflib/algorithm/DiffException.java
similarity index 100%
rename from src/main/java/com/github/difflib/algorithm/DiffException.java
rename to java-diff-utils/src/main/java/com/github/difflib/algorithm/DiffException.java
diff --git a/src/main/java/com/github/difflib/algorithm/DifferentiationFailedException.java b/java-diff-utils/src/main/java/com/github/difflib/algorithm/DifferentiationFailedException.java
similarity index 93%
rename from src/main/java/com/github/difflib/algorithm/DifferentiationFailedException.java
rename to java-diff-utils/src/main/java/com/github/difflib/algorithm/DifferentiationFailedException.java
index 682a9b484fa33ffe5d35259d2af1143b033f01a4..e201e440a8b586ce94473797ce0583e22fcc1c48 100644
--- a/src/main/java/com/github/difflib/algorithm/DifferentiationFailedException.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/algorithm/DifferentiationFailedException.java
@@ -18,8 +18,8 @@ package com.github.difflib.algorithm;
 /**
  * Thrown whenever the differencing engine cannot produce the differences between two revisions of ta text.
  *
- * @see MyersDiff
- * @see difflib.DiffAlgorithm
+ * @see com.github.difflib.algorithm.myers.MyersDiff
+ * @see DiffAlgorithmI
  */
 public class DifferentiationFailedException extends DiffException {
 
diff --git a/src/main/java/com/github/difflib/algorithm/myers/MyersDiff.java b/java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/MyersDiff.java
similarity index 99%
rename from src/main/java/com/github/difflib/algorithm/myers/MyersDiff.java
rename to java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/MyersDiff.java
index 8c1834117cb3adcdcf46878140fd45a957bad6b6..abcf31e229f06cc538157bd38d9b896a341e34c8 100644
--- a/src/main/java/com/github/difflib/algorithm/myers/MyersDiff.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/MyersDiff.java
@@ -138,7 +138,7 @@ public final class MyersDiff<T> implements DiffAlgorithmI<T> {
     /**
      * Constructs a {@link Patch} from a difference path.
      *
-     * @param path The path.
+     * @param actualPath The path.
      * @param orig The original sequence.
      * @param rev The revised sequence.
      * @return A {@link Patch} script corresponding to the path.
diff --git a/src/main/java/com/github/difflib/algorithm/myers/PathNode.java b/java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/PathNode.java
similarity index 87%
rename from src/main/java/com/github/difflib/algorithm/myers/PathNode.java
rename to java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/PathNode.java
index a3f2070c3697ea25aedb6e644405094e9e5e8ef8..fe8fd03aff8e133847e255373a3ca20c78274464 100644
--- a/src/main/java/com/github/difflib/algorithm/myers/PathNode.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/PathNode.java
@@ -19,10 +19,6 @@ package com.github.difflib.algorithm.myers;
  * A node in a diffpath.
  *
  * @author <a href="mailto:juanco@suigeneris.org">Juanco Anez</a>
- *
- * @see DiffNode
- * @see Snake
- *
  */
 public final class PathNode {
 
@@ -78,10 +74,10 @@ public final class PathNode {
     }
 
     /**
-     * Skips sequences of {@link DiffNode DiffNodes} until a {@link Snake} or bootstrap node is found, or the end of the
+     * Skips sequences of {@link PathNode PathNodes} until a snake or bootstrap node is found, or the end of the
      * path is reached.
      *
-     * @return The next first {@link Snake} or bootstrap node in the path, or <code>null</code> if none found.
+     * @return The next first {@link PathNode} or bootstrap node in the path, or <code>null</code> if none found.
      */
     public final PathNode previousSnake() {
         if (isBootstrap()) {
@@ -102,9 +98,9 @@ public final class PathNode {
         PathNode node = this;
         while (node != null) {
             buf.append("(");
-            buf.append(Integer.toString(node.i));
+            buf.append(node.i);
             buf.append(",");
-            buf.append(Integer.toString(node.j));
+            buf.append(node.j);
             buf.append(")");
             node = node.prev;
         }
diff --git a/src/main/java/com/github/difflib/patch/AbstractDelta.java b/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java
similarity index 100%
rename from src/main/java/com/github/difflib/patch/AbstractDelta.java
rename to java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java
diff --git a/src/main/java/com/github/difflib/patch/ChangeDelta.java b/java-diff-utils/src/main/java/com/github/difflib/patch/ChangeDelta.java
similarity index 97%
rename from src/main/java/com/github/difflib/patch/ChangeDelta.java
rename to java-diff-utils/src/main/java/com/github/difflib/patch/ChangeDelta.java
index 43f68b3546f489e8ac6b6224a3db4943deadf598..7c1cd98bb95347819a71c9b5824f9c544b4883d7 100644
--- a/src/main/java/com/github/difflib/patch/ChangeDelta.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/patch/ChangeDelta.java
@@ -22,7 +22,7 @@ import java.util.Objects;
  * Describes the change-delta between original and revised texts.
  *
  * @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
- * @param T The type of the compared elements in the data 'lines'.
+ * @param <T> The type of the compared elements in the data 'lines'.
  */
 public final class ChangeDelta<T> extends AbstractDelta<T> {
 
diff --git a/src/main/java/com/github/difflib/patch/Chunk.java b/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java
similarity index 90%
rename from src/main/java/com/github/difflib/patch/Chunk.java
rename to java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java
index ed70e20d9ec1af9d9d6007a23156df36d9b59025..70b57223caab109e3c3adde41dc98667fc1a694e 100644
--- a/src/main/java/com/github/difflib/patch/Chunk.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java
@@ -15,6 +15,7 @@
  */
 package com.github.difflib.patch;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
@@ -23,13 +24,14 @@ import java.util.Objects;
  * Holds the information about the part of text involved in the diff process
  *
  * <p>
- * Text is represented as <code>Object[]</code> because the diff engine is capable of handling more than plain ascci. In
- * fact, arrays or lists of any type that implements {@link java.lang.Object#hashCode hashCode()} and
- * {@link java.lang.Object#equals equals()} correctly can be subject to differencing using this library.
+ * Text is represented as <code>Object[]</code> because the diff engine is capable of handling more
+ * than plain ascci. In fact, arrays or lists of any type that implements
+ * {@link java.lang.Object#hashCode hashCode()} and {@link java.lang.Object#equals equals()}
+ * correctly can be subject to differencing using this library.
  * </p>
  *
  * @author <a href="dm.naumenko@gmail.com>Dmitry Naumenko</a>
- * @param T The type of the compared elements in the 'lines'.
+ * @param <T> The type of the compared elements in the 'lines'.
  */
 public final class Chunk<T> {
 
@@ -44,7 +46,7 @@ public final class Chunk<T> {
      */
     public Chunk(int position, List<T> lines) {
         this.position = position;
-        this.lines = lines;
+        this.lines = new ArrayList<>(lines);
     }
 
     /**
diff --git a/src/main/java/com/github/difflib/patch/DeleteDelta.java b/java-diff-utils/src/main/java/com/github/difflib/patch/DeleteDelta.java
similarity index 96%
rename from src/main/java/com/github/difflib/patch/DeleteDelta.java
rename to java-diff-utils/src/main/java/com/github/difflib/patch/DeleteDelta.java
index 66a928cab8ac3cdd8457b7054020514a6f77efbb..cfbc427cc455cd05183ae5095ea08fc6e1f03df3 100644
--- a/src/main/java/com/github/difflib/patch/DeleteDelta.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/patch/DeleteDelta.java
@@ -21,7 +21,7 @@ import java.util.List;
  * Describes the delete-delta between original and revised texts.
  *
  * @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
- * @param T The type of the compared elements in the 'lines'.
+ * @param <T> The type of the compared elements in the 'lines'.
  */
 public final class DeleteDelta<T> extends AbstractDelta<T> {
 
diff --git a/src/main/java/com/github/difflib/patch/DeltaType.java b/java-diff-utils/src/main/java/com/github/difflib/patch/DeltaType.java
similarity index 100%
rename from src/main/java/com/github/difflib/patch/DeltaType.java
rename to java-diff-utils/src/main/java/com/github/difflib/patch/DeltaType.java
diff --git a/src/main/java/com/github/difflib/patch/DiffException.java b/java-diff-utils/src/main/java/com/github/difflib/patch/DiffException.java
similarity index 100%
rename from src/main/java/com/github/difflib/patch/DiffException.java
rename to java-diff-utils/src/main/java/com/github/difflib/patch/DiffException.java
diff --git a/src/main/java/com/github/difflib/patch/InsertDelta.java b/java-diff-utils/src/main/java/com/github/difflib/patch/InsertDelta.java
similarity index 96%
rename from src/main/java/com/github/difflib/patch/InsertDelta.java
rename to java-diff-utils/src/main/java/com/github/difflib/patch/InsertDelta.java
index 08de5f1f2a18c3de43a0c5e8f5963f490aa31e1d..12164fa481ead79413eae856e61ded6fa763eb95 100644
--- a/src/main/java/com/github/difflib/patch/InsertDelta.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/patch/InsertDelta.java
@@ -21,7 +21,7 @@ import java.util.List;
  * Describes the add-delta between original and revised texts.
  *
  * @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
- * @param T The type of the compared elements in the 'lines'.
+ * @param <T> The type of the compared elements in the 'lines'.
  */
 public final class InsertDelta<T> extends AbstractDelta<T> {
 
diff --git a/src/main/java/com/github/difflib/patch/Patch.java b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java
similarity index 92%
rename from src/main/java/com/github/difflib/patch/Patch.java
rename to java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java
index 9bbb30dc8606ff72307141d6b76f0046b2d039b0..688e9f16ec758f682ab98d4d8a67c0ce73374935 100644
--- a/src/main/java/com/github/difflib/patch/Patch.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java
@@ -19,12 +19,9 @@ limitations under the License.
  */
 package com.github.difflib.patch;
 
+import static java.util.Comparator.comparing;
 import com.github.difflib.algorithm.Change;
-import static com.github.difflib.patch.DeltaType.DELETE;
-import static com.github.difflib.patch.DeltaType.INSERT;
 import java.util.ArrayList;
-import java.util.Collections;
-import static java.util.Comparator.comparing;
 import java.util.List;
 import java.util.ListIterator;
 
@@ -32,7 +29,7 @@ import java.util.ListIterator;
  * Describes the patch holding all deltas between the original and revised texts.
  *
  * @author <a href="dm.naumenko@gmail.com">Dmitry Naumenko</a>
- * @param T The type of the compared elements in the 'lines'.
+ * @param <T> The type of the compared elements in the 'lines'.
  */
 public final class Patch<T> {
 
@@ -93,7 +90,7 @@ public final class Patch<T> {
      * @return the deltas
      */
     public List<AbstractDelta<T>> getDeltas() {
-        Collections.sort(deltas, comparing(d -> d.getSource().getPosition()));
+        deltas.sort(comparing(d -> d.getSource().getPosition()));
         return deltas;
     }
 
diff --git a/src/main/java/com/github/difflib/patch/PatchFailedException.java b/java-diff-utils/src/main/java/com/github/difflib/patch/PatchFailedException.java
similarity index 100%
rename from src/main/java/com/github/difflib/patch/PatchFailedException.java
rename to java-diff-utils/src/main/java/com/github/difflib/patch/PatchFailedException.java
diff --git a/src/main/java/com/github/difflib/text/DiffRow.java b/java-diff-utils/src/main/java/com/github/difflib/text/DiffRow.java
similarity index 100%
rename from src/main/java/com/github/difflib/text/DiffRow.java
rename to java-diff-utils/src/main/java/com/github/difflib/text/DiffRow.java
diff --git a/src/main/java/com/github/difflib/text/DiffRowGenerator.java b/java-diff-utils/src/main/java/com/github/difflib/text/DiffRowGenerator.java
similarity index 87%
rename from src/main/java/com/github/difflib/text/DiffRowGenerator.java
rename to java-diff-utils/src/main/java/com/github/difflib/text/DiffRowGenerator.java
index 2ed2662ee5a907bf06dc9771e5aac3618949e483..4f963cd7d4f477a1be75c9be0462c085719e1210 100644
--- a/src/main/java/com/github/difflib/text/DiffRowGenerator.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/text/DiffRowGenerator.java
@@ -29,6 +29,7 @@ import java.util.function.BiPredicate;
 import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import static java.util.stream.Collectors.toList;
 
 /**
  * This class for generating DiffRows for side-by-sidy view. You can customize the way of generating. For example, show
@@ -42,13 +43,16 @@ import java.util.regex.Pattern;
  *      ignoreWhiteSpaces(true).columnWidth(100).build();
  * </code>
  */
-public class DiffRowGenerator {
+public final class DiffRowGenerator {
 
     public static final BiPredicate<String, String> DEFAULT_EQUALIZER = Object::equals;
 
     public static final BiPredicate<String, String> IGNORE_WHITESPACE_EQUALIZER = (original, revised)
             -> adjustWhitespace(original).equals(adjustWhitespace(revised));
 
+    public static final Function<String, String> LINE_NORMALIZER_FOR_HTML = StringUtils::normalize;
+
+
     /**
      * Splitting lines by character to achieve char by char diff checking.
      */
@@ -60,6 +64,7 @@ public class DiffRowGenerator {
         return list;
     };
     public static final Pattern SPLIT_BY_WORD_PATTERN = Pattern.compile("\\s+|[,.\\[\\](){}/\\\\*+\\-#]");
+
     /**
      * Splitting lines by word to achieve word by word diff checking.
      */
@@ -98,8 +103,7 @@ public class DiffRowGenerator {
      *
      * @param startPosition the position from which tag should start. The counting start from a zero.
      * @param endPosition the position before which tag should should be closed.
-     * @param tag the tag name without angle brackets, just a word
-     * @param cssClass the optional css class
+     * @param tagGenerator the tag generator
      */
     static void wrapInTag(List<String> sequence, int startPosition,
             int endPosition, Function<Boolean, String> tagGenerator) {
@@ -145,6 +149,7 @@ public class DiffRowGenerator {
     private final Function<Boolean, String> newTag;
     private final Function<Boolean, String> oldTag;
     private final boolean reportLinesUnchanged;
+    private final Function<String, String> lineNormalizer;
 
     private final boolean showInlineDiffs;
 
@@ -158,8 +163,10 @@ public class DiffRowGenerator {
         inlineDiffSplitter = builder.inlineDiffSplitter;
         equalizer = ignoreWhiteSpaces ? IGNORE_WHITESPACE_EQUALIZER : DEFAULT_EQUALIZER;
         reportLinesUnchanged = builder.reportLinesUnchanged;
+        lineNormalizer = builder.lineNormalizer;
 
         Objects.requireNonNull(inlineDiffSplitter);
+        Objects.requireNonNull(lineNormalizer);
     }
 
     /**
@@ -179,7 +186,6 @@ public class DiffRowGenerator {
      * for displaying side-by-side diff.
      *
      * @param original the original text
-     * @param revised the revised text
      * @param patch the given patch
      * @return the DiffRows between original and revised texts
      */
@@ -187,8 +193,7 @@ public class DiffRowGenerator {
         List<DiffRow> diffRows = new ArrayList<>();
         int endPos = 0;
         final List<AbstractDelta<String>> deltaList = patch.getDeltas();
-        for (int i = 0; i < deltaList.size(); i++) {
-            AbstractDelta<String> delta = deltaList.get(i);
+        for (AbstractDelta<String> delta : deltaList) {
             Chunk<String> orig = delta.getSource();
             Chunk<String> rev = delta.getTarget();
 
@@ -199,7 +204,7 @@ public class DiffRowGenerator {
             // Inserted DiffRow
             if (delta instanceof InsertDelta) {
                 endPos = orig.last() + 1;
-                for (String line : (List<String>) rev.getLines()) {
+                for (String line : rev.getLines()) {
                     diffRows.add(buildDiffRow(Tag.INSERT, "", line));
                 }
                 continue;
@@ -208,7 +213,7 @@ public class DiffRowGenerator {
             // Deleted DiffRow
             if (delta instanceof DeleteDelta) {
                 endPos = orig.last() + 1;
-                for (String line : (List<String>) orig.getLines()) {
+                for (String line : orig.getLines()) {
                     diffRows.add(buildDiffRow(Tag.DELETE, line, ""));
                 }
                 continue;
@@ -261,14 +266,22 @@ public class DiffRowGenerator {
                 StringUtils.wrapText(newline, columnWidth));
     }
 
+    List<String> normalizeLines(List<String> list) {
+        return reportLinesUnchanged
+                ? list
+                : list.stream()
+                        .map(lineNormalizer::apply)
+                        .collect(toList());
+    }
+
     /**
      * Add the inline diffs for given delta
      *
      * @param delta the given delta
      */
     private List<DiffRow> generateInlineDiffs(AbstractDelta<String> delta) throws DiffException {
-        List<String> orig = StringUtils.normalize(delta.getSource().getLines());
-        List<String> rev = StringUtils.normalize(delta.getTarget().getLines());
+        List<String> orig = normalizeLines(delta.getSource().getLines());
+        List<String> rev = normalizeLines(delta.getTarget().getLines());
         List<String> origList;
         List<String> revList;
         String joinedOrig = String.join("\n", orig);
@@ -337,9 +350,9 @@ public class DiffRowGenerator {
 
     private String preprocessLine(String line) {
         if (columnWidth == 0) {
-            return StringUtils.normalize(line);
+            return lineNormalizer.apply(line);
         } else {
-            return StringUtils.wrapText(StringUtils.normalize(line), columnWidth);
+            return StringUtils.wrapText(lineNormalizer.apply(line), columnWidth);
         }
     }
 
@@ -361,6 +374,7 @@ public class DiffRowGenerator {
         private boolean mergeOriginalRevised = false;
         private boolean reportLinesUnchanged = false;
         private Function<String, List<String>> inlineDiffSplitter = SPLITTER_BY_CHARACTER;
+        private Function<String, String> lineNormalizer = LINE_NORMALIZER_FOR_HTML;
 
         private Builder() {
         }
@@ -388,7 +402,8 @@ public class DiffRowGenerator {
         }
 
         /**
-         * Give the originial old and new text lines to Diffrow without any additional processing.
+         * Give the originial old and new text lines to Diffrow without any additional processing and without any tags to
+         * highlight the change.
          *
          * @param val the value to set. Default: false.
          * @return builder with configured reportLinesUnWrapped parameter
@@ -401,7 +416,7 @@ public class DiffRowGenerator {
         /**
          * Generator for Old-Text-Tags.
          *
-         * @param tag the tag to set. Without angle brackets. Default: span.
+         * @param generator the tag generator
          * @return builder with configured ignoreBlankLines parameter
          */
         public Builder oldTag(Function<Boolean, String> generator) {
@@ -421,7 +436,7 @@ public class DiffRowGenerator {
         }
 
         /**
-         * Set the column with of generated lines of original and revised texts.
+         * Set the column width of generated lines of original and revised texts.
          *
          * @param width the width to set. Making it < 0 doesn't have any sense. Default 80. @return builder with config
          * ured ignoreBlankLines parameter
@@ -454,17 +469,41 @@ public class DiffRowGenerator {
         }
 
         /**
-         * Per default each character is separatly processed. This variant introduces processing by word, which should
-         * deliver no in word changes.
+         * Per default each character is separatly processed. This variant introduces processing by word, which does not
+         * deliver in word changes. Therefore the whole word will be tagged as changed:
+         *
+         * <pre>
+         * false:    (aBa : aba) --  changed: a(B)a : a(b)a
+         * true:     (aBa : aba) --  changed: (aBa) : (aba)
+         * </pre>
          */
         public Builder inlineDiffByWord(boolean inlineDiffByWord) {
             inlineDiffSplitter = inlineDiffByWord ? SPLITTER_BY_WORD : SPLITTER_BY_CHARACTER;
             return this;
         }
 
+        /**
+         * To provide some customized splitting a splitter can be provided. Here someone could think about sentence splitter,
+         * comma splitter or stuff like that.
+         *
+         * @param inlineDiffSplitter
+         * @return
+         */
         public Builder inlineDiffBySplitter(Function<String, List<String>> inlineDiffSplitter) {
             this.inlineDiffSplitter = inlineDiffSplitter;
             return this;
         }
+
+        /**
+         * By default DiffRowGenerator preprocesses lines for HTML output. Tabs and special HTML characters like "&lt;"
+         * are replaced with its encoded value. To change this you can provide a customized line normalizer here.
+         *
+         * @param lineNormalizer
+         * @return
+         */
+        public Builder lineNormalizer(Function<String, String> lineNormalizer) {
+            this.lineNormalizer = lineNormalizer;
+            return this;
+        }
     }
 }
diff --git a/src/main/java/com/github/difflib/text/StringUtils.java b/java-diff-utils/src/main/java/com/github/difflib/text/StringUtils.java
similarity index 92%
rename from src/main/java/com/github/difflib/text/StringUtils.java
rename to java-diff-utils/src/main/java/com/github/difflib/text/StringUtils.java
index 3a2dab054a9d4e59ba82373e4e4b1ac0c56e12da..a142548876c0692b1382f7d80fed7ff3ae8a9cbb 100644
--- a/src/main/java/com/github/difflib/text/StringUtils.java
+++ b/java-diff-utils/src/main/java/com/github/difflib/text/StringUtils.java
@@ -34,12 +34,6 @@ final class StringUtils {
         return htmlEntites(str).replace("\t", "    ");
     }
 
-    public static List<String> normalize(List<String> list) {
-        return list.stream()
-                .map(StringUtils::normalize)
-                .collect(toList());
-    }
-
     public static List<String> wrapText(List<String> list, int columnWidth) {
         return list.stream()
                 .map(line -> wrapText(line, columnWidth))
diff --git a/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiff.java b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiff.java
new file mode 100644
index 0000000000000000000000000000000000000000..bdabc5892d01359b4f8af00f00478feaa0f23d0f
--- /dev/null
+++ b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiff.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2019 java-diff-utils.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.difflib.unifieddiff;
+
+import com.github.difflib.patch.PatchFailedException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ *
+ * @author Tobias Warneke (t.warneke@gmx.net)
+ */
+public final class UnifiedDiff {
+
+    private String header;
+    private String tail;
+    private final List<UnifiedDiffFile> files = new ArrayList<>();
+
+    public String getHeader() {
+        return header;
+    }
+
+    public void setHeader(String header) {
+        this.header = header;
+    }
+
+    void addFile(UnifiedDiffFile file) {
+        files.add(file);
+    }
+
+    public List<UnifiedDiffFile> getFiles() {
+        return Collections.unmodifiableList(files);
+    }
+
+    void setTailTxt(String tailTxt) {
+        this.tail = tailTxt;
+    }
+
+    public String getTail() {
+        return tail;
+    }
+
+    public List<String> spplyPatchTo(Predicate<String> findFile, List<String> originalLines) throws PatchFailedException {
+        UnifiedDiffFile file = files.stream()
+                .filter(diff -> findFile.test(diff.getFromFile()))
+                .findFirst().orElse(null);
+        if (file != null) {
+            return file.getPatch().applyTo(originalLines);
+        } else {
+            return originalLines;
+        }
+    }
+
+    public static UnifiedDiff from(String header, String tail, UnifiedDiffFile... files) {
+        UnifiedDiff diff = new UnifiedDiff();
+        diff.setHeader(header);
+        diff.setTailTxt(tail);
+        for (UnifiedDiffFile file : files) {
+            diff.addFile(file);
+        }
+        return diff;
+    }
+}
diff --git a/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffFile.java b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffFile.java
new file mode 100644
index 0000000000000000000000000000000000000000..c244184fcab6a751eb0ce021f9131693dded51a9
--- /dev/null
+++ b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffFile.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2019 java-diff-utils.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.difflib.unifieddiff;
+
+import com.github.difflib.patch.Patch;
+
+/**
+ *
+ * @author Tobias Warneke (t.warneke@gmx.net)
+ */
+public final class UnifiedDiffFile {
+
+    private String diffCommand;
+    private String fromFile;
+    private String fromTimestamp;
+    private String toFile;
+    private String toTimestamp;
+    private String index;
+    private Patch<String> patch = new Patch<>();
+
+    public String getDiffCommand() {
+        return diffCommand;
+    }
+
+    public void setDiffCommand(String diffCommand) {
+        this.diffCommand = diffCommand;
+    }
+
+    public String getFromFile() {
+        return fromFile;
+    }
+
+    public void setFromFile(String fromFile) {
+        this.fromFile = fromFile;
+    }
+
+    public String getToFile() {
+        return toFile;
+    }
+
+    public void setToFile(String toFile) {
+        this.toFile = toFile;
+    }
+
+    public void setIndex(String index) {
+        this.index = index;
+    }
+
+    public String getIndex() {
+        return index;
+    }
+
+    public Patch<String> getPatch() {
+        return patch;
+    }
+
+    public String getFromTimestamp() {
+        return fromTimestamp;
+    }
+
+    public void setFromTimestamp(String fromTimestamp) {
+        this.fromTimestamp = fromTimestamp;
+    }
+
+    public String getToTimestamp() {
+        return toTimestamp;
+    }
+
+    public void setToTimestamp(String toTimestamp) {
+        this.toTimestamp = toTimestamp;
+    }
+    
+    
+
+    public static UnifiedDiffFile from(String fromFile, String toFile, Patch<String> patch) {
+        UnifiedDiffFile file = new UnifiedDiffFile();
+        file.setFromFile(fromFile);
+        file.setToFile(toFile);
+        file.patch = patch;
+        return file;
+    }
+}
diff --git a/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffParserException.java b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffParserException.java
new file mode 100644
index 0000000000000000000000000000000000000000..ab7114db363dfe1aee3ce40b2cd603d6af8d94eb
--- /dev/null
+++ b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffParserException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 java-diff-utils.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.difflib.unifieddiff;
+
+/**
+ *
+ * @author Tobias Warneke (t.warneke@gmx.net)
+ */
+public class UnifiedDiffParserException extends RuntimeException {
+
+    public UnifiedDiffParserException() {
+    }
+
+    public UnifiedDiffParserException(String message) {
+        super(message);
+    }
+
+    public UnifiedDiffParserException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public UnifiedDiffParserException(Throwable cause) {
+        super(cause);
+    }
+
+    public UnifiedDiffParserException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+
+}
diff --git a/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffReader.java b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..c208786ca37a293d5a8a485541d52c40707789f7
--- /dev/null
+++ b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffReader.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2019 java-diff-utils.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.difflib.unifieddiff;
+
+import com.github.difflib.patch.ChangeDelta;
+import com.github.difflib.patch.Chunk;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.BiConsumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * @author Tobias Warneke (t.warneke@gmx.net)
+ */
+public final class UnifiedDiffReader {
+
+    static final Pattern UNIFIED_DIFF_CHUNK_REGEXP = Pattern.compile("^@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@");
+    static final Pattern TIMESTAMP_REGEXP = Pattern.compile("(\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}\\.\\d{3,})");
+
+    private final InternalUnifiedDiffReader READER;
+    private final UnifiedDiff data = new UnifiedDiff();
+
+    private final UnifiedDiffLine DIFF_COMMAND = new UnifiedDiffLine(true, "^diff\\s", this::processDiff);
+    private final UnifiedDiffLine INDEX = new UnifiedDiffLine(true, "^index\\s[\\da-zA-Z]+\\.\\.[\\da-zA-Z]+(\\s(\\d+))?$", this::processIndex);
+    private final UnifiedDiffLine FROM_FILE = new UnifiedDiffLine(true, "^---\\s", this::processFromFile);
+    private final UnifiedDiffLine TO_FILE = new UnifiedDiffLine(true, "^\\+\\+\\+\\s", this::processToFile);
+
+    private final UnifiedDiffLine CHUNK = new UnifiedDiffLine(false, UNIFIED_DIFF_CHUNK_REGEXP, this::processChunk);
+    private final UnifiedDiffLine LINE_NORMAL = new UnifiedDiffLine("^\\s", this::processNormalLine);
+    private final UnifiedDiffLine LINE_DEL = new UnifiedDiffLine("^-", this::processDelLine);
+    private final UnifiedDiffLine LINE_ADD = new UnifiedDiffLine("^\\+", this::processAddLine);
+
+    private UnifiedDiffFile actualFile;
+
+    UnifiedDiffReader(Reader reader) {
+        this.READER = new InternalUnifiedDiffReader(reader);
+    }
+
+    // schema = [[/^\s+/, normal], [/^diff\s/, start], [/^new file mode \d+$/, new_file], 
+    // [/^deleted file mode \d+$/, deleted_file], [/^index\s[\da-zA-Z]+\.\.[\da-zA-Z]+(\s(\d+))?$/, index], 
+    // [/^---\s/, from_file], [/^\+\+\+\s/, to_file], [/^@@\s+\-(\d+),?(\d+)?\s+\+(\d+),?(\d+)?\s@@/, chunk], 
+    // [/^-/, del], [/^\+/, add], [/^\\ No newline at end of file$/, eof]];
+    private UnifiedDiff parse() throws IOException, UnifiedDiffParserException {
+        String headerTxt = "";
+        LOG.log(Level.INFO, "header parsing");
+        String line = null;
+        while (READER.ready()) {
+            line = READER.readLine();
+            LOG.log(Level.INFO, "parsing line {0}", line);
+            if (DIFF_COMMAND.validLine(line) || INDEX.validLine(line)
+                    || FROM_FILE.validLine(line) || TO_FILE.validLine(line)) {
+                break;
+            } else {
+                headerTxt += line + "\n";
+            }
+        }
+        if (!"".equals(headerTxt)) {
+            data.setHeader(headerTxt);
+        }
+
+        while (line != null) {
+            if (!CHUNK.validLine(line)) {
+                initFileIfNecessary();
+                while (!CHUNK.validLine(line)) {
+                    if (processLine(line, DIFF_COMMAND, INDEX, FROM_FILE, TO_FILE) == false) {
+                        throw new UnifiedDiffParserException("expected file start line not found");
+                    }
+                    line = READER.readLine();
+                }
+            }
+            processLine(line, CHUNK);
+            while ((line = READER.readLine()) != null) {
+                if (processLine(line, LINE_NORMAL, LINE_ADD, LINE_DEL) == false) {
+                    throw new UnifiedDiffParserException("expected data line not found");
+                }
+                if (originalTxt.size() == old_size && revisedTxt.size() == new_size) {
+                    finalizeChunk();
+                    break;
+                }
+            }
+            line = READER.readLine();
+            if (line == null || line.startsWith("--")) {
+                break;
+            }
+        }
+
+        if (READER.ready()) {
+            String tailTxt = "";
+            while (READER.ready()) {
+                tailTxt += READER.readLine() + "\n";
+            }
+            data.setTailTxt(tailTxt);
+        }
+
+        return data;
+    }
+
+    static String[] parseFileNames(String line) {
+        String[] split = line.split(" ");
+        return new String[]{
+            split[2].replaceAll("^a/", ""),
+            split[3].replaceAll("^b/", "")
+        };
+    }
+
+    private static final Logger LOG = Logger.getLogger(UnifiedDiffReader.class.getName());
+
+    public static UnifiedDiff parseUnifiedDiff(InputStream stream) throws IOException, UnifiedDiffParserException {
+        UnifiedDiffReader parser = new UnifiedDiffReader(new BufferedReader(new InputStreamReader(stream)));
+        return parser.parse();
+    }
+
+    private boolean processLine(String line, UnifiedDiffLine... rules) throws UnifiedDiffParserException {
+        for (UnifiedDiffLine rule : rules) {
+            if (rule.processLine(line)) {
+                LOG.info("  >>> processed rule " + rule.toString());
+                return true;
+            }
+        }
+        LOG.info("  >>> no rule matched " + line);
+        return false;
+        //throw new UnifiedDiffParserException("parsing error at line " + line);
+    }
+
+    private void initFileIfNecessary() {
+        if (!originalTxt.isEmpty() || !revisedTxt.isEmpty()) {
+            throw new IllegalStateException();
+        }
+        actualFile = null;
+        if (actualFile == null) {
+            actualFile = new UnifiedDiffFile();
+            data.addFile(actualFile);
+        }
+    }
+
+    private void processDiff(MatchResult match, String line) {
+        //initFileIfNecessary();
+        LOG.log(Level.INFO, "start {0}", line);
+        String[] fromTo = parseFileNames(READER.lastLine());
+        actualFile.setFromFile(fromTo[0]);
+        actualFile.setToFile(fromTo[1]);
+        actualFile.setDiffCommand(line);
+    }
+
+    private List<String> originalTxt = new ArrayList<>();
+    private List<String> revisedTxt = new ArrayList<>();
+    private int old_ln;
+    private int old_size;
+    private int new_ln;
+    private int new_size;
+
+    private void finalizeChunk() {
+        if (!originalTxt.isEmpty() || !revisedTxt.isEmpty()) {
+            actualFile.getPatch().addDelta(new ChangeDelta<>(new Chunk<>(
+                    old_ln - 1, originalTxt), new Chunk<>(
+                    new_ln - 1, revisedTxt)));
+            old_ln = 0;
+            new_ln = 0;
+            originalTxt.clear();
+            revisedTxt.clear();
+        }
+    }
+
+    private void processNormalLine(MatchResult match, String line) {
+        String cline = line.substring(1);
+        originalTxt.add(cline);
+        revisedTxt.add(cline);
+    }
+
+    private void processAddLine(MatchResult match, String line) {
+        String cline = line.substring(1);
+        revisedTxt.add(cline);
+    }
+
+    private void processDelLine(MatchResult match, String line) {
+        String cline = line.substring(1);
+        originalTxt.add(cline);
+    }
+
+    private void processChunk(MatchResult match, String chunkStart) {
+        // finalizeChunk();
+        old_ln = toInteger(match, 1, 1);
+        old_size = toInteger(match, 2, 0);
+        new_ln = toInteger(match, 3, 1);
+        new_size = toInteger(match, 4, 0);
+        if (old_ln == 0) {
+            old_ln = 1;
+        }
+        if (new_ln == 0) {
+            new_ln = 1;
+        }
+    }
+
+    private static Integer toInteger(MatchResult match, int group, int defValue) throws NumberFormatException {
+        return Integer.valueOf(Objects.toString(match.group(group), "" + defValue));
+    }
+
+    private void processIndex(MatchResult match, String line) {
+        //initFileIfNecessary();
+        LOG.log(Level.INFO, "index {0}", line);
+        actualFile.setIndex(line.substring(6));
+    }
+
+    private void processFromFile(MatchResult match, String line) {
+        //initFileIfNecessary();
+        actualFile.setFromFile(extractFileName(line));
+        actualFile.setFromTimestamp(extractTimestamp(line));
+    }
+
+    private void processToFile(MatchResult match, String line) {
+        //initFileIfNecessary();
+        actualFile.setToFile(extractFileName(line));
+        actualFile.setToTimestamp(extractTimestamp(line));
+    }
+
+    private String extractFileName(String _line) {
+        Matcher matcher = TIMESTAMP_REGEXP.matcher(_line);
+        String line = _line;
+        if (matcher.find()) {
+            line = line.substring(1, matcher.start());
+        }
+        return line.substring(4).replaceFirst("^(a|b)\\/", "")
+                .replace(TIMESTAMP_REGEXP.toString(), "").trim();
+    }
+
+    private String extractTimestamp(String line) {
+        Matcher matcher = TIMESTAMP_REGEXP.matcher(line);
+        if (matcher.find()) {
+            return matcher.group();
+        }
+        return null;
+    }
+
+    final class UnifiedDiffLine {
+
+        private final Pattern pattern;
+        private final BiConsumer<MatchResult, String> command;
+        private final boolean stopsHeaderParsing;
+
+        public UnifiedDiffLine(String pattern, BiConsumer<MatchResult, String> command) {
+            this(false, pattern, command);
+        }
+
+        public UnifiedDiffLine(boolean stopsHeaderParsing, String pattern, BiConsumer<MatchResult, String> command) {
+            this.pattern = Pattern.compile(pattern);
+            this.command = command;
+            this.stopsHeaderParsing = stopsHeaderParsing;
+        }
+
+        public UnifiedDiffLine(boolean stopsHeaderParsing, Pattern pattern, BiConsumer<MatchResult, String> command) {
+            this.pattern = pattern;
+            this.command = command;
+            this.stopsHeaderParsing = stopsHeaderParsing;
+        }
+
+        public boolean validLine(String line) {
+            Matcher m = pattern.matcher(line);
+            return m.find();
+        }
+
+        public boolean processLine(String line) throws UnifiedDiffParserException {
+            Matcher m = pattern.matcher(line);
+            if (m.find()) {
+                command.accept(m.toMatchResult(), line);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        public boolean isStopsHeaderParsing() {
+            return stopsHeaderParsing;
+        }
+
+        @Override
+        public String toString() {
+            return "UnifiedDiffLine{" + "pattern=" + pattern + ", stopsHeaderParsing=" + stopsHeaderParsing + '}';
+        }
+    }
+}
+
+class InternalUnifiedDiffReader extends BufferedReader {
+
+    private String lastLine;
+
+    public InternalUnifiedDiffReader(Reader reader) {
+        super(reader);
+    }
+
+    @Override
+    public String readLine() throws IOException {
+        lastLine = super.readLine();
+        return lastLine();
+    }
+
+    String lastLine() {
+        return lastLine;
+    }
+}
diff --git a/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffWriter.java b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9488f9c0aac7474aec91e0fde6842437e8b6f8f
--- /dev/null
+++ b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/UnifiedDiffWriter.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2019 java-diff-utils.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.difflib.unifieddiff;
+
+import com.github.difflib.patch.AbstractDelta;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @todo use an instance to store contextSize and originalLinesProvider.
+ * @author Tobias Warneke (t.warneke@gmx.net)
+ */
+public class UnifiedDiffWriter {
+
+    private static final Logger LOG = Logger.getLogger(UnifiedDiffWriter.class.getName());
+
+    public static void write(UnifiedDiff diff, Function<String, List<String>> originalLinesProvider, Writer writer, int contextSize) throws IOException {
+        write(diff, originalLinesProvider, line -> {
+            try {
+                writer.append(line).append("\n");
+            } catch (IOException ex) {
+                LOG.log(Level.SEVERE, null, ex);
+            }
+        }, contextSize);
+    }
+
+    public static void write(UnifiedDiff diff, Function<String, List<String>> originalLinesProvider, Consumer<String> writer, int contextSize) throws IOException {
+        writer.accept(diff.getHeader());
+
+        for (UnifiedDiffFile file : diff.getFiles()) {
+            List<AbstractDelta<String>> patchDeltas = new ArrayList<>(
+                    file.getPatch().getDeltas());
+            if (!patchDeltas.isEmpty()) {
+                writeOrNothing(writer, file.getDiffCommand());
+                if (file.getIndex() != null) {
+                    writer.accept("index " + file.getIndex());
+                }
+                if (file.getFromFile() != null) {
+                    writer.accept("--- " + file.getFromFile());
+                }
+                if (file.getToFile() != null) {
+                    writer.accept("+++ " + file.getToFile());
+                }
+
+                List<String> originalLines = originalLinesProvider.apply(file.getFromFile());
+
+                List<AbstractDelta<String>> deltas = new ArrayList<>();
+
+                AbstractDelta<String> delta = patchDeltas.get(0);
+                deltas.add(delta); // add the first Delta to the current set
+                // if there's more than 1 Delta, we may need to output them together
+                if (patchDeltas.size() > 1) {
+                    for (int i = 1; i < patchDeltas.size(); i++) {
+                        int position = delta.getSource().getPosition();
+
+                        // Check if the next Delta is too close to the current
+                        // position.
+                        // And if it is, add it to the current set
+                        AbstractDelta<String> nextDelta = patchDeltas.get(i);
+                        if ((position + delta.getSource().size() + contextSize) >= (nextDelta
+                                .getSource().getPosition() - contextSize)) {
+                            deltas.add(nextDelta);
+                        } else {
+                            // if it isn't, output the current set,
+                            // then create a new set and add the current Delta to
+                            // it.
+                            processDeltas(writer, originalLines, deltas, contextSize);
+                            deltas.clear();
+                            deltas.add(nextDelta);
+                        }
+                        delta = nextDelta;
+                    }
+
+                }
+                // don't forget to process the last set of Deltas
+                processDeltas(writer, originalLines, deltas, contextSize);
+            }
+
+        }
+        if (diff.getTail() != null) {
+            writer.accept("--");
+            writer.accept(diff.getTail());
+        }
+    }
+
+    private static void processDeltas(Consumer<String> writer,
+            List<String> origLines, List<AbstractDelta<String>> deltas,
+            int contextSize) {
+        List<String> buffer = new ArrayList<>();
+        int origTotal = 0; // counter for total lines output from Original
+        int revTotal = 0; // counter for total lines output from Original
+        int line;
+
+        AbstractDelta<String> curDelta = deltas.get(0);
+
+        // NOTE: +1 to overcome the 0-offset Position
+        int origStart = curDelta.getSource().getPosition() + 1 - contextSize;
+        if (origStart < 1) {
+            origStart = 1;
+        }
+
+        int revStart = curDelta.getTarget().getPosition() + 1 - contextSize;
+        if (revStart < 1) {
+            revStart = 1;
+        }
+
+        // find the start of the wrapper context code
+        int contextStart = curDelta.getSource().getPosition() - contextSize;
+        if (contextStart < 0) {
+            contextStart = 0; // clamp to the start of the file
+        }
+
+        // output the context before the first Delta
+        for (line = contextStart; line < curDelta.getSource().getPosition(); line++) { //
+            buffer.add(" " + origLines.get(line));
+            origTotal++;
+            revTotal++;
+        }
+        // output the first Delta
+        getDeltaText(txt -> buffer.add(txt), curDelta);
+        origTotal += curDelta.getSource().getLines().size();
+        revTotal += curDelta.getTarget().getLines().size();
+
+        int deltaIndex = 1;
+        while (deltaIndex < deltas.size()) { // for each of the other Deltas
+            AbstractDelta<String> nextDelta = deltas.get(deltaIndex);
+            int intermediateStart = curDelta.getSource().getPosition()
+                    + curDelta.getSource().getLines().size();
+            for (line = intermediateStart; line < nextDelta.getSource()
+                    .getPosition(); line++) {
+                // output the code between the last Delta and this one
+                buffer.add(" " + origLines.get(line));
+                origTotal++;
+                revTotal++;
+            }
+            getDeltaText(txt -> buffer.add(txt), nextDelta); // output the Delta
+            origTotal += nextDelta.getSource().getLines().size();
+            revTotal += nextDelta.getTarget().getLines().size();
+            curDelta = nextDelta;
+            deltaIndex++;
+        }
+
+        // Now output the post-Delta context code, clamping the end of the file
+        contextStart = curDelta.getSource().getPosition()
+                + curDelta.getSource().getLines().size();
+        for (line = contextStart; (line < (contextStart + contextSize))
+                && (line < origLines.size()); line++) {
+            buffer.add(" " + origLines.get(line));
+            origTotal++;
+            revTotal++;
+        }
+
+        // Create and insert the block header, conforming to the Unified Diff
+        // standard
+        writer.accept("@@ -" + origStart + "," + origTotal + " +" + revStart + "," + revTotal + " @@");
+        buffer.forEach(txt -> {
+            writer.accept(txt);
+        });
+    }
+
+    /**
+     * getDeltaText returns the lines to be added to the Unified Diff text from the Delta parameter
+     *
+     * @param delta - the Delta to output
+     * @return list of String lines of code.
+     * @author Bill James (tankerbay@gmail.com)
+     */
+    private static void getDeltaText(Consumer<String> writer, AbstractDelta<String> delta) {
+        for (String line : delta.getSource().getLines()) {
+            writer.accept("-" + line);
+        }
+        for (String line : delta.getTarget().getLines()) {
+            writer.accept("+" + line);
+        }
+    }
+
+    private static void writeOrNothing(Consumer<String> writer, String str) throws IOException {
+        if (str != null) {
+            writer.accept(str);
+        }
+    }
+}
diff --git a/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/package-info.java b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..49edc6874171bbb79d30e07ca84071a91a90ac91
--- /dev/null
+++ b/java-diff-utils/src/main/java/com/github/difflib/unifieddiff/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2019 java-diff-utils.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This is the first test version of a multifile diff parser. The Api is still subject
+ * of change.
+ */
+package com.github.difflib.unifieddiff;
diff --git a/src/test/java/com/github/difflib/DiffUtilsTest.java b/java-diff-utils/src/test/java/com/github/difflib/DiffUtilsTest.java
similarity index 100%
rename from src/test/java/com/github/difflib/DiffUtilsTest.java
rename to java-diff-utils/src/test/java/com/github/difflib/DiffUtilsTest.java
diff --git a/src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java b/java-diff-utils/src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java
similarity index 92%
rename from src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java
rename to java-diff-utils/src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java
index 89ab32f65575341eb91eb8cdff1aeab7fd6ee97f..c6bfc4eca2f546eb11e5680f4ab113b343cf8f71 100644
--- a/src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java
+++ b/java-diff-utils/src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java
@@ -10,13 +10,14 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import static org.junit.Assert.assertTrue;
+import static java.util.stream.Collectors.joining;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import org.junit.Test;
 
 public class GenerateUnifiedDiffTest {
 
-    private static List<String> fileToLines(String filename) throws FileNotFoundException, IOException {
+    public static List<String> fileToLines(String filename) throws FileNotFoundException, IOException {
         List<String> lines = new ArrayList<>();
         String line = "";
         try (BufferedReader in = new BufferedReader(new FileReader(filename))) {
@@ -110,11 +111,13 @@ public class GenerateUnifiedDiffTest {
         List<String> unifiedDiff = UnifiedDiffUtils.generateUnifiedDiff(originalFile, revisedFile,
                 origLines, patch, 10);
 
+        System.out.println(unifiedDiff.stream().collect(joining("\n")));
+
         Patch<String> fromUnifiedPatch = UnifiedDiffUtils.parseUnifiedDiff(unifiedDiff);
         List<String> patchedLines;
         try {
-            patchedLines = (List<String>) fromUnifiedPatch.applyTo(origLines);
-            assertTrue(revLines.size() == patchedLines.size());
+            patchedLines = fromUnifiedPatch.applyTo(origLines);
+            assertEquals(revLines.size(), patchedLines.size());
             for (int i = 0; i < revLines.size(); i++) {
                 String l1 = revLines.get(i);
                 String l2 = patchedLines.get(i);
diff --git a/src/test/java/com/github/difflib/TestConstants.java b/java-diff-utils/src/test/java/com/github/difflib/TestConstants.java
similarity index 82%
rename from src/test/java/com/github/difflib/TestConstants.java
rename to java-diff-utils/src/test/java/com/github/difflib/TestConstants.java
index 56f5bf665b34ac78030c3de79bcca5fe6a62e256..ba6d754e5ce9df5a12eabe77a6560dc019d2bd46 100644
--- a/src/test/java/com/github/difflib/TestConstants.java
+++ b/java-diff-utils/src/test/java/com/github/difflib/TestConstants.java
@@ -10,7 +10,7 @@ public final class TestConstants {
 
     public static final String BASE_FOLDER_RESOURCES = "target/test-classes/";
     /**
-     * The base folder containing the test files. Ends with {@link #FS}.
+     * The base folder containing the test files.
      */
     public static final String MOCK_FOLDER = BASE_FOLDER_RESOURCES + "/mocks/";
 
diff --git a/src/test/java/com/github/difflib/algorithm/myers/MyersDiffTest.java b/java-diff-utils/src/test/java/com/github/difflib/algorithm/myers/MyersDiffTest.java
similarity index 100%
rename from src/test/java/com/github/difflib/algorithm/myers/MyersDiffTest.java
rename to java-diff-utils/src/test/java/com/github/difflib/algorithm/myers/MyersDiffTest.java
diff --git a/src/test/java/com/github/difflib/examples/ApplyPatch.java b/java-diff-utils/src/test/java/com/github/difflib/examples/ApplyPatch.java
similarity index 100%
rename from src/test/java/com/github/difflib/examples/ApplyPatch.java
rename to java-diff-utils/src/test/java/com/github/difflib/examples/ApplyPatch.java
diff --git a/src/test/java/com/github/difflib/examples/ComputeDifference.java b/java-diff-utils/src/test/java/com/github/difflib/examples/ComputeDifference.java
similarity index 100%
rename from src/test/java/com/github/difflib/examples/ComputeDifference.java
rename to java-diff-utils/src/test/java/com/github/difflib/examples/ComputeDifference.java
diff --git a/src/test/java/com/github/difflib/patch/PatchTest.java b/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java
similarity index 100%
rename from src/test/java/com/github/difflib/patch/PatchTest.java
rename to java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java
diff --git a/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java b/java-diff-utils/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java
similarity index 89%
rename from src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java
rename to java-diff-utils/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java
index f9e5c710a5a756d21943bafe8675b889131954ca..c667b251885acee8b119f01fab94e9c00992b90d 100644
--- a/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java
+++ b/java-diff-utils/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java
@@ -5,6 +5,7 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.regex.Pattern;
 import static java.util.stream.Collectors.toList;
@@ -28,6 +29,16 @@ public class DiffRowGeneratorTest {
         assertEquals(3, rows.size());
     }
 
+    /**
+     * Test of normalize method, of class StringUtils.
+     */
+    @Test
+    public void testNormalize_List() {
+        DiffRowGenerator generator = DiffRowGenerator.create()
+                .build();
+        assertEquals(Collections.singletonList("    test"), generator.normalizeLines(Collections.singletonList("\ttest")));
+    }
+
     @Test
     public void testGenerator_Default2() throws DiffException {
         String first = "anything \n \nother";
@@ -375,4 +386,35 @@ public class DiffRowGeneratorTest {
         assertEquals("[[CHANGE,This is a test ~senctence~.,This is a test **for diffutils**.], [CHANGE,,**This is the second line.**], [CHANGE,,**And one more.**]]",
                 rows.toString());
     }
+
+    @Test
+    public void testGeneratorIssue41DefaultNormalizer() throws DiffException {
+        DiffRowGenerator generator = DiffRowGenerator.create()
+                .build();
+        List<DiffRow> rows = generator.generateDiffRows(Arrays.asList("<"), Arrays.asList("<"));
+        assertEquals("[[EQUAL,&lt;,&lt;]]", rows.toString());
+    }
+
+    @Test
+    public void testGeneratorIssue41UserNormalizer() throws DiffException {
+        DiffRowGenerator generator = DiffRowGenerator.create()
+                .lineNormalizer(str -> str.replace("\t", "    "))
+                .build();
+        List<DiffRow> rows = generator.generateDiffRows(Arrays.asList("<"), Arrays.asList("<"));
+        assertEquals("[[EQUAL,<,<]]", rows.toString());
+        rows = generator.generateDiffRows(Arrays.asList("\t<"), Arrays.asList("<"));
+        assertEquals("[[CHANGE,    <,<]]", rows.toString());
+    }
+
+    @Test
+    public void testGenerationIssue44reportLinesUnchangedProblem() throws DiffException {
+        DiffRowGenerator generator = DiffRowGenerator.create()
+                .showInlineDiffs(true)
+                .reportLinesUnchanged(true)
+                .oldTag(f -> "~~")
+                .newTag(f -> "**")
+                .build();
+        List<DiffRow> rows = generator.generateDiffRows(Arrays.asList("<dt>To do</dt>"), Arrays.asList("<dt>Done</dt>"));
+        assertEquals("[[CHANGE,<dt>~~T~~o~~ do~~</dt>,<dt>**D**o**ne**</dt>]]", rows.toString());
+    }
 }
diff --git a/src/test/java/com/github/difflib/text/StringUtilsTest.java b/java-diff-utils/src/test/java/com/github/difflib/text/StringUtilsTest.java
similarity index 85%
rename from src/test/java/com/github/difflib/text/StringUtilsTest.java
rename to java-diff-utils/src/test/java/com/github/difflib/text/StringUtilsTest.java
index 2b120d09e871fd0e1d67788af0c2363cab008573..93076cc1d6df8609886395d467f0443ea0f575a3 100644
--- a/src/test/java/com/github/difflib/text/StringUtilsTest.java
+++ b/java-diff-utils/src/test/java/com/github/difflib/text/StringUtilsTest.java
@@ -15,7 +15,6 @@
  */
 package com.github.difflib.text;
 
-import java.util.Collections;
 import static org.junit.Assert.*;
 import org.junit.Test;
 
@@ -41,14 +40,6 @@ public class StringUtilsTest {
         assertEquals("    test", StringUtils.normalize("\ttest"));
     }
 
-    /**
-     * Test of normalize method, of class StringUtils.
-     */
-    @Test
-    public void testNormalize_List() {
-        assertEquals(Collections.singletonList("    test"), StringUtils.normalize(Collections.singletonList("\ttest")));
-    }
-
     /**
      * Test of wrapText method, of class StringUtils.
      */
diff --git a/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffReaderTest.java b/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffReaderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb0280db458adfef173db0df31a406eec6b3b2bf
--- /dev/null
+++ b/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffReaderTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2019 java-diff-utils.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.difflib.unifieddiff;
+
+import com.github.difflib.patch.AbstractDelta;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import static org.assertj.core.api.Java6Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+/**
+ *
+ * @author Tobias Warneke (t.warneke@gmx.net)
+ */
+public class UnifiedDiffReaderTest {
+
+    @Test
+    public void testSimpleParse() throws IOException {
+        UnifiedDiff diff = UnifiedDiffReader.parseUnifiedDiff(UnifiedDiffReaderTest.class.getResourceAsStream("jsqlparser_patch_1.diff"));
+
+        System.out.println(diff);
+
+        assertThat(diff.getFiles().size()).isEqualTo(2);
+
+        UnifiedDiffFile file1 = diff.getFiles().get(0);
+        assertThat(file1.getFromFile()).isEqualTo("src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt");
+        assertThat(file1.getPatch().getDeltas().size()).isEqualTo(3);
+
+        assertThat(diff.getTail()).isEqualTo("2.17.1.windows.2\n\n");
+    }
+
+    @Test
+    public void testParseDiffBlock() {
+        String[] files = UnifiedDiffReader.parseFileNames("diff --git a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java");
+        assertThat(files).containsExactly("src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java", "src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java");
+    }
+
+    @Test
+    public void testChunkHeaderParsing() {
+        Pattern pattern = UnifiedDiffReader.UNIFIED_DIFF_CHUNK_REGEXP;
+        Matcher matcher = pattern.matcher("@@ -189,6 +189,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */");
+
+        assertTrue(matcher.find());
+        assertEquals("189", matcher.group(1));
+        assertEquals("189", matcher.group(3));
+    }
+
+    @Test
+    public void testChunkHeaderParsing2() {
+        //"^@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@.*$"
+        Pattern pattern = UnifiedDiffReader.UNIFIED_DIFF_CHUNK_REGEXP;
+        Matcher matcher = pattern.matcher("@@ -189,6 +189,7 @@");
+
+        assertTrue(matcher.find());
+        assertEquals("189", matcher.group(1));
+        assertEquals("189", matcher.group(3));
+    }
+
+    @Test
+    public void testChunkHeaderParsing3() {
+        //"^@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@.*$"
+        Pattern pattern = UnifiedDiffReader.UNIFIED_DIFF_CHUNK_REGEXP;
+        Matcher matcher = pattern.matcher("@@ -1,27 +1,27 @@");
+
+        assertTrue(matcher.find());
+        assertEquals("1", matcher.group(1));
+        assertEquals("1", matcher.group(3));
+    }
+
+    @Test
+    public void testSimpleParse2() throws IOException {
+        UnifiedDiff diff = UnifiedDiffReader.parseUnifiedDiff(UnifiedDiffReaderTest.class.getResourceAsStream("jsqlparser_patch_1.diff"));
+
+        System.out.println(diff);
+
+        assertThat(diff.getFiles().size()).isEqualTo(2);
+
+        UnifiedDiffFile file1 = diff.getFiles().get(0);
+        assertThat(file1.getFromFile()).isEqualTo("src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt");
+        assertThat(file1.getPatch().getDeltas().size()).isEqualTo(3);
+
+        AbstractDelta<String> first = file1.getPatch().getDeltas().get(0);
+
+        assertThat(first.getSource().size()).isGreaterThan(0);
+        assertThat(first.getTarget().size()).isGreaterThan(0);
+
+        assertThat(diff.getTail()).isEqualTo("2.17.1.windows.2\n\n");
+    }
+
+    @Test
+    public void testSimplePattern() {
+        Pattern pattern = Pattern.compile("^\\+\\+\\+\\s");
+
+        Matcher m = pattern.matcher("+++ revised.txt");
+        assertTrue(m.find());
+    }
+
+    @Test
+    public void testParseIssue46() throws IOException {
+        UnifiedDiff diff = UnifiedDiffReader.parseUnifiedDiff(
+                UnifiedDiffReaderTest.class.getResourceAsStream("problem_diff_issue46.diff"));
+
+        System.out.println(diff);
+
+        assertThat(diff.getFiles().size()).isEqualTo(1);
+
+        UnifiedDiffFile file1 = diff.getFiles().get(0);
+        assertThat(file1.getFromFile()).isEqualTo(".vhd");
+        assertThat(file1.getPatch().getDeltas().size()).isEqualTo(1);
+
+        assertThat(diff.getTail()).isNull();
+    }
+
+    @Test
+    public void testParseIssue33() throws IOException {
+        UnifiedDiff diff = UnifiedDiffReader.parseUnifiedDiff(
+                UnifiedDiffReaderTest.class.getResourceAsStream("problem_diff_issue33.diff"));
+
+        assertThat(diff.getFiles().size()).isEqualTo(1);
+
+        UnifiedDiffFile file1 = diff.getFiles().get(0);
+        assertThat(file1.getFromFile()).isEqualTo("Main.java");
+        assertThat(file1.getPatch().getDeltas().size()).isEqualTo(1);
+
+        assertThat(diff.getTail()).isNull();
+        assertThat(diff.getHeader()).isNull();
+    }
+}
diff --git a/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffRoundTripTest.java b/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffRoundTripTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8fc82fc632f486b2e7e59144cc369bd3aac23b5
--- /dev/null
+++ b/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffRoundTripTest.java
@@ -0,0 +1,167 @@
+package com.github.difflib.unifieddiff;
+
+import com.github.difflib.DiffUtils;
+import com.github.difflib.TestConstants;
+import com.github.difflib.algorithm.DiffException;
+import com.github.difflib.patch.Patch;
+import com.github.difflib.patch.PatchFailedException;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import static java.util.stream.Collectors.joining;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class UnifiedDiffRoundTripTest {
+
+    public static List<String> fileToLines(String filename) throws FileNotFoundException, IOException {
+        List<String> lines = new ArrayList<>();
+        String line = "";
+        try (BufferedReader in = new BufferedReader(new FileReader(filename))) {
+            while ((line = in.readLine()) != null) {
+                lines.add(line);
+            }
+        }
+        return lines;
+    }
+
+    @Test
+    public void testGenerateUnified() throws DiffException, IOException {
+        List<String> origLines = fileToLines(TestConstants.MOCK_FOLDER + "original.txt");
+        List<String> revLines = fileToLines(TestConstants.MOCK_FOLDER + "revised.txt");
+
+        verify(origLines, revLines, "original.txt", "revised.txt");
+    }
+
+    @Test
+    public void testGenerateUnifiedWithOneDelta() throws DiffException, IOException {
+        List<String> origLines = fileToLines(TestConstants.MOCK_FOLDER + "one_delta_test_original.txt");
+        List<String> revLines = fileToLines(TestConstants.MOCK_FOLDER + "one_delta_test_revised.txt");
+
+        verify(origLines, revLines, "one_delta_test_original.txt", "one_delta_test_revised.txt");
+    }
+
+    @Test
+    public void testGenerateUnifiedDiffWithoutAnyDeltas() throws DiffException, IOException {
+        List<String> test = Arrays.asList("abc");
+        Patch<String> patch = DiffUtils.diff(test, test);
+        StringWriter writer = new StringWriter();
+
+        UnifiedDiffWriter.write(
+                UnifiedDiff.from("header", "tail", UnifiedDiffFile.from("abc", "abc", patch)),
+                name -> test,
+                writer, 0);
+
+        System.out.println(writer);
+    }
+
+    @Test
+    public void testDiff_Issue10() throws IOException {
+        final List<String> baseLines = fileToLines(TestConstants.MOCK_FOLDER + "issue10_base.txt");
+        final List<String> patchLines = fileToLines(TestConstants.MOCK_FOLDER + "issue10_patch.txt");
+
+        UnifiedDiff unifiedDiff = UnifiedDiffReader.parseUnifiedDiff(
+                new ByteArrayInputStream(patchLines.stream().collect(joining("\n")).getBytes())
+        );
+
+        final Patch<String> p = unifiedDiff.getFiles().get(0).getPatch();
+        try {
+            DiffUtils.patch(baseLines, p);
+        } catch (PatchFailedException e) {
+            fail(e.getMessage());
+        }
+    }
+
+    /**
+     * Issue 12
+     */
+    @Test
+    @Ignore
+    public void testPatchWithNoDeltas() throws DiffException, IOException {
+        final List<String> lines1 = fileToLines(TestConstants.MOCK_FOLDER + "issue11_1.txt");
+        final List<String> lines2 = fileToLines(TestConstants.MOCK_FOLDER + "issue11_2.txt");
+        verify(lines1, lines2, "issue11_1.txt", "issue11_2.txt");
+    }
+
+    @Test
+    public void testDiff5() throws DiffException, IOException {
+        final List<String> lines1 = fileToLines(TestConstants.MOCK_FOLDER + "5A.txt");
+        final List<String> lines2 = fileToLines(TestConstants.MOCK_FOLDER + "5B.txt");
+        verify(lines1, lines2, "5A.txt", "5B.txt");
+    }
+
+    /**
+     * Issue 19
+     */
+    @Test
+    public void testDiffWithHeaderLineInText() throws DiffException, IOException {
+        List<String> original = new ArrayList<>();
+        List<String> revised = new ArrayList<>();
+
+        original.add("test line1");
+        original.add("test line2");
+        original.add("test line 4");
+        original.add("test line 5");
+
+        revised.add("test line1");
+        revised.add("test line2");
+        revised.add("@@ -2,6 +2,7 @@");
+        revised.add("test line 4");
+        revised.add("test line 5");
+
+        Patch<String> patch = DiffUtils.diff(original, revised);
+        StringWriter writer = new StringWriter();
+        UnifiedDiffWriter.write(
+                UnifiedDiff.from("header", "tail", UnifiedDiffFile.from("original", "revised", patch)),
+                name -> original,
+                writer, 10);
+
+        System.out.println(writer.toString());
+
+        UnifiedDiff unifiedDiff = UnifiedDiffReader.parseUnifiedDiff(new ByteArrayInputStream(writer.toString().getBytes()));
+    }
+
+    private void verify(List<String> origLines, List<String> revLines,
+            String originalFile, String revisedFile) throws DiffException, IOException {
+        Patch<String> patch = DiffUtils.diff(origLines, revLines);
+
+        StringWriter writer = new StringWriter();
+        UnifiedDiffWriter.write(
+                UnifiedDiff.from("header", "tail", UnifiedDiffFile.from(originalFile, revisedFile, patch)),
+                name -> origLines,
+                writer, 10);
+
+        System.out.println(writer.toString());
+
+        UnifiedDiff unifiedDiff = UnifiedDiffReader.parseUnifiedDiff(new ByteArrayInputStream(writer.toString().getBytes()));
+
+        List<String> patchedLines;
+        try {
+//            if (unifiedDiff.getFiles().isEmpty()) {
+//                patchedLines = new ArrayList<>(origLines);
+//            } else {
+//                Patch<String> fromUnifiedPatch = unifiedDiff.getFiles().get(0).getPatch();
+//                patchedLines = fromUnifiedPatch.applyTo(origLines);
+//            }
+            patchedLines = unifiedDiff.spplyPatchTo(file -> originalFile.equals(file), origLines);
+            assertEquals(revLines.size(), patchedLines.size());
+            for (int i = 0; i < revLines.size(); i++) {
+                String l1 = revLines.get(i);
+                String l2 = patchedLines.get(i);
+                if (!l1.equals(l2)) {
+                    fail("Line " + (i + 1) + " of the patched file did not match the revised original");
+                }
+            }
+        } catch (PatchFailedException e) {
+            fail(e.getMessage());
+        }
+    }
+}
diff --git a/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffWriterTest.java b/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffWriterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1972ab3978d85a8e0ed341b11e5e1637d1afce8d
--- /dev/null
+++ b/java-diff-utils/src/test/java/com/github/difflib/unifieddiff/UnifiedDiffWriterTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 java-diff-utils.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.difflib.unifieddiff;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import org.junit.Test;
+
+/**
+ *
+ * @author Tobias Warneke (t.warneke@gmx.net)
+ */
+public class UnifiedDiffWriterTest {
+
+    public UnifiedDiffWriterTest() {
+    }
+
+    @Test
+    public void testWrite() throws URISyntaxException, IOException {
+        String str = readFile(UnifiedDiffReaderTest.class.getResource("jsqlparser_patch_1.diff").toURI(), Charset.defaultCharset());
+        UnifiedDiff diff = UnifiedDiffReader.parseUnifiedDiff(new ByteArrayInputStream(str.getBytes()));
+
+        StringWriter writer = new StringWriter();
+//        UnifiedDiffWriter.write(diff, writer);
+//        System.out.println(writer.toString());
+    }
+
+    static String readFile(URI path, Charset encoding)
+            throws IOException {
+        byte[] encoded = Files.readAllBytes(Paths.get(path));
+        return new String(encoded, encoding);
+    }
+}
diff --git a/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/jsqlparser_patch_1.diff b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/jsqlparser_patch_1.diff
new file mode 100644
index 0000000000000000000000000000000000000000..c868759e9f9fafc70588ed4cf1adcc92e68e0270
--- /dev/null
+++ b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/jsqlparser_patch_1.diff
@@ -0,0 +1,61 @@
+From 3209a16c55c1976d5b772c607fd4b9d5fb9f9483 Mon Sep 17 00:00:00 2001
+From: wumpz <t.warneke@gmx.net>
+Date: Tue, 19 Feb 2019 01:35:14 +0100
+Subject: [PATCH] fixes #753
+
+---
+ src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt  | 5 +++--
+ .../net/sf/jsqlparser/statement/select/SelectTest.java     | 7 +++++++
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
+index cd9bcd1..5f4b2b7 100644
+--- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
++++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
+@@ -189,6 +189,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
+ |   <K_JOIN:"JOIN">
+ |   <K_KEEP:"KEEP">
+ |   <K_KEY:"KEY">
++|   <K_FN:"FN">
+ |   <K_LAST: "LAST">
+ |   <K_LATERAL:"LATERAL">
+ |   <K_LEADING:"LEADING">
+@@ -1039,7 +1040,7 @@ String RelObjectNameWithoutValue() :
+       | tk=<K_INSERT> | tk=<K_INDEX> | tk=<K_PRIMARY> | tk=<K_ENABLE>
+ 	  | tk=<K_UNSIGNED>
+       | tk=<K_TEMP> | tk=<K_TEMPORARY> | tk=<K_TYPE> | tk=<K_ISNULL> 
+-	  | tk=<K_ZONE> | tk=<K_COLUMNS> | tk=<K_DESCRIBE>
++	  | tk=<K_ZONE> | tk=<K_COLUMNS> | tk=<K_DESCRIBE> | tk=<K_FN>
+ /*      | tk=<K_PLACING> | tk=<K_BOTH> | tk=<K_LEADING> | tk=<K_TRAILING> */
+       )
+ 
+@@ -3118,7 +3119,7 @@ Function Function() #Function:
+     Expression expr1 = null;
+ }
+ {
+-    ["{fn" { retval.setEscaped(true); } ]
++    ["{" <K_FN> { retval.setEscaped(true); } ]
+ 
+     funcName=RelObjectNameExt()
+ 
+diff --git a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java
+index 7ee9b38..d39bfd3 100644
+--- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java
++++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java
+@@ -1063,6 +1063,13 @@ public class SelectTest {
+         assertSqlCanBeParsedAndDeparsed("SELECT {fn concat(a, b)} AS COL");
+     }
+ 
++    @Test
++    public void testEscapedFunctionsIssue753() throws JSQLParserException {
++        Statement stmt = CCJSqlParserUtil.parse("SELECT { fn test(0)} AS COL");
++        assertEquals("SELECT {fn test(0)} AS COL", stmt.toString());
++        assertSqlCanBeParsedAndDeparsed("SELECT fn FROM fn");
++    }
++
+     @Test
+     public void testNamedParametersPR702() throws JSQLParserException {
+         assertSqlCanBeParsedAndDeparsed("SELECT substring(id, 2, 3), substring(id from 2 for 3), substring(id from 2), trim(BOTH ' ' from 'foo bar '), trim(LEADING ' ' from 'foo bar '), trim(TRAILING ' ' from 'foo bar '), trim(' ' from 'foo bar '), position('foo' in 'bar'), overlay('foo' placing 'bar' from 1), overlay('foo' placing 'bar' from 1 for 2) FROM my table");
+-- 
+2.17.1.windows.2
+
diff --git a/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue33.diff b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue33.diff
new file mode 100644
index 0000000000000000000000000000000000000000..a78344dd283cb20c4836d0295d1cb330a48ef2fc
--- /dev/null
+++ b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue33.diff
@@ -0,0 +1,6 @@
+--- a/Main.java
++++ b/Main.java
+@@ -2,2 +2,3 @@ public class Main {
+     public static void main(String[] args) {
++        System.out.println("Hello, world!");
+     }
\ No newline at end of file
diff --git a/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue46.diff b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue46.diff
new file mode 100644
index 0000000000000000000000000000000000000000..ef6886948940080ff82c2bd79cca71e5c75ea753
--- /dev/null
+++ b/java-diff-utils/src/test/resources/com/github/difflib/unifieddiff/problem_diff_issue46.diff
@@ -0,0 +1,8 @@
+--- a.vhd	2019-04-18 13:49:39.516149751 +0200
++++ b.vhd	2019-04-18 11:33:08.372563078 +0200
+@@ -2819,3 +2819,2 @@
+--- some comment
+-bla
+-bla
++
++
\ No newline at end of file
diff --git a/src/test/resources/mocks/5A.txt b/java-diff-utils/src/test/resources/mocks/5A.txt
similarity index 96%
rename from src/test/resources/mocks/5A.txt
rename to java-diff-utils/src/test/resources/mocks/5A.txt
index 96e51b80277cf159e72ee0dbf76a7e870df6a0f2..69eafd8b445e35da2ba5d64d18adad49d1e81479 100644
--- a/src/test/resources/mocks/5A.txt
+++ b/java-diff-utils/src/test/resources/mocks/5A.txt
@@ -1,372 +1,372 @@
-#!/bin/sh
-#
-# Copyright (c) 2006 Johannes E. Schindelin
-#
-
-test_description='Test special whitespace in diff engine.
-
-'
-. ./test-lib.sh
-. ../diff-lib.sh
-
-# Ray Lehtiniemi's example
-
-cat << EOF > x
-do {
-   nothing;
-} while (0);
-EOF
-
-git update-index --add x
-
-cat << EOF > x
-do
-{
-   nothing;
-}
-while (0);
-EOF
-
-cat << EOF > expect
-diff --git a/x b/x
-index adf3937..6edc172 100644
---- a/x
-+++ b/x
-@@ -1,3 +1,5 @@
--do {
-+do
-+{
-    nothing;
--} while (0);
-+}
-+while (0);
-EOF
-
-git diff > out
-test_expect_success "Ray's example without options" 'test_cmp expect out'
-
-git diff -w > out
-test_expect_success "Ray's example with -w" 'test_cmp expect out'
-
-git diff -b > out
-test_expect_success "Ray's example with -b" 'test_cmp expect out'
-
-tr 'Q' '\015' << EOF > x
-whitespace at beginning
-whitespace change
-whitespace in the middle
-whitespace at end
-unchanged line
-CR at endQ
-EOF
-
-git update-index x
-
-tr '_' ' ' << EOF > x
-                whitespace at beginning
-whitespace          change
-white space in the middle
-whitespace at end__
-unchanged line
-CR at end
-EOF
-
-tr 'Q_' '\015 ' << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
---- a/x
-+++ b/x
-@@ -1,6 +1,6 @@
--whitespace at beginning
--whitespace change
--whitespace in the middle
--whitespace at end
-+             whitespace at beginning
-+whitespace       change
-+white space in the middle
-+whitespace at end__
-unchanged line
--CR at endQ
-+CR at end
-EOF
-git diff > out
-test_expect_success 'another test, without options' 'test_cmp expect out'
-
-cat << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
-EOF
-git diff -w > out
-test_expect_success 'another test, with -w' 'test_cmp expect out'
-
-tr 'Q' '\015' << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
---- a/x
-+++ b/x
-@@ -1,6 +1,6 @@
--whitespace at beginning
-+             whitespace at beginning
-whitespace change
--whitespace in the middle
-+white space in the middle
-whitespace at end
-unchanged line
-CR at endQ
-EOF
-git diff -b > out
-test_expect_success 'another test, with -b' 'test_cmp expect out'
-
-test_expect_success 'check mixed spaces and tabs in indent' '
-
-                # This is indented with SP HT SP.
-                echo "    foo();" > x &&
-                git diff --check | grep "space before tab in indent"
-
-'
-
-test_expect_success 'check mixed tabs and spaces in indent' '
-
-                # This is indented with HT SP HT.
-                echo "                  foo();" > x &&
-                git diff --check | grep "space before tab in indent"
-
-'
-
-test_expect_success 'check with no whitespace errors' '
-
-                git commit -m "snapshot" &&
-                echo "foo();" > x &&
-                git diff --check
-
-'
-
-test_expect_success 'check with trailing whitespace' '
-
-                echo "foo(); " > x &&
-                test_must_fail git diff --check
-
-'
-
-test_expect_success 'check with space before tab in indent' '
-
-                # indent has space followed by hard tab
-                echo "   foo();" > x &&
-                test_must_fail git diff --check
-
-'
-
-test_expect_success '--check and --exit-code are not exclusive' '
-
-                git checkout x &&
-                git diff --check --exit-code
-
-'
-
-test_expect_success '--check and --quiet are not exclusive' '
-
-                git diff --check --quiet
-
-'
-
-test_expect_success 'check staged with no whitespace errors' '
-
-                echo "foo();" > x &&
-                git add x &&
-                git diff --cached --check
-
-'
-
-test_expect_success 'check staged with trailing whitespace' '
-
-                echo "foo(); " > x &&
-                git add x &&
-                test_must_fail git diff --cached --check
-
-'
-
-test_expect_success 'check staged with space before tab in indent' '
-
-                # indent has space followed by hard tab
-                echo "   foo();" > x &&
-                git add x &&
-                test_must_fail git diff --cached --check
-
-'
-
-test_expect_success 'check with no whitespace errors (diff-index)' '
-
-                echo "foo();" > x &&
-                git add x &&
-                git diff-index --check HEAD
-
-'
-
-test_expect_success 'check with trailing whitespace (diff-index)' '
-
-                echo "foo(); " > x &&
-                git add x &&
-                test_must_fail git diff-index --check HEAD
-
-'
-
-test_expect_success 'check with space before tab in indent (diff-index)' '
-
-                # indent has space followed by hard tab
-                echo "   foo();" > x &&
-                git add x &&
-                test_must_fail git diff-index --check HEAD
-
-'
-
-test_expect_success 'check staged with no whitespace errors (diff-index)' '
-
-                echo "foo();" > x &&
-                git add x &&
-                git diff-index --cached --check HEAD
-
-'
-
-test_expect_success 'check staged with trailing whitespace (diff-index)' '
-
-                echo "foo(); " > x &&
-                git add x &&
-                test_must_fail git diff-index --cached --check HEAD
-
-'
-
-test_expect_success 'check staged with space before tab in indent (diff-index)' '
-
-                # indent has space followed by hard tab
-                echo "   foo();" > x &&
-                git add x &&
-                test_must_fail git diff-index --cached --check HEAD
-
-'
-
-test_expect_success 'check with no whitespace errors (diff-tree)' '
-
-                echo "foo();" > x &&
-                git commit -m "new commit" x &&
-                git diff-tree --check HEAD^ HEAD
-
-'
-
-test_expect_success 'check with trailing whitespace (diff-tree)' '
-
-                echo "foo(); " > x &&
-                git commit -m "another commit" x &&
-                test_must_fail git diff-tree --check HEAD^ HEAD
-
-'
-
-test_expect_success 'check with space before tab in indent (diff-tree)' '
-
-                # indent has space followed by hard tab
-                echo "   foo();" > x &&
-                git commit -m "yet another" x &&
-                test_must_fail git diff-tree --check HEAD^ HEAD
-
-'
-
-test_expect_success 'check trailing whitespace (trailing-space: off)' '
-
-                git config core.whitespace "-trailing-space" &&
-                echo "foo ();   " > x &&
-                git diff --check
-
-'
-
-test_expect_success 'check trailing whitespace (trailing-space: on)' '
-
-                git config core.whitespace "trailing-space" &&
-                echo "foo ();   " > x &&
-                test_must_fail git diff --check
-
-'
-
-test_expect_success 'check space before tab in indent (space-before-tab: off)' '
-
-                # indent contains space followed by HT
-                git config core.whitespace "-space-before-tab" &&
-                echo "   foo ();" > x &&
-                git diff --check
-
-'
-
-test_expect_success 'check space before tab in indent (space-before-tab: on)' '
-
-                # indent contains space followed by HT
-                git config core.whitespace "space-before-tab" &&
-                echo "   foo ();   " > x &&
-                test_must_fail git diff --check
-
-'
-
-test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '
-
-                git config core.whitespace "-indent-with-non-tab"
-                echo "        foo ();" > x &&
-                git diff --check
-
-'
-
-test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '
-
-                git config core.whitespace "indent-with-non-tab" &&
-                echo "        foo ();" > x &&
-                test_must_fail git diff --check
-
-'
-
-test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '
-
-                git config core.whitespace "indent-with-non-tab" &&
-                echo "                   foo ();" > x &&
-                test_must_fail git diff --check
-
-'
-
-test_expect_success 'line numbers in --check output are correct' '
-
-                echo "" > x &&
-                echo "foo(); " >> x &&
-                git diff --check | grep "x:2:"
-
-'
-
-test_expect_success 'checkdiff detects trailing blank lines' '
-                echo "foo();" >x &&
-                echo "" >>x &&
-                git diff --check | grep "ends with blank"
-'
-
-test_expect_success 'checkdiff allows new blank lines' '
-                git checkout x &&
-                mv x y &&
-                (
-                                echo "/* This is new */" &&
-                                echo "" &&
-                                cat y
-                ) >x &&
-                git diff --check
-'
-
-test_expect_success 'combined diff with autocrlf conversion' '
-
-                git reset --hard &&
-                echo >x hello &&
-                git commit -m "one side" x &&
-                git checkout HEAD^ &&
-                echo >x goodbye &&
-                git commit -m "the other side" x &&
-                git config core.autocrlf true &&
-                test_must_fail git merge master &&
-
-                git diff | sed -e "1,/^@@@/d" >actual &&
-                ! grep "^-" actual
-
-'
-
-test_done
-
+#!/bin/sh
+#
+# Copyright (c) 2006 Johannes E. Schindelin
+#
+
+test_description='Test special whitespace in diff engine.
+
+'
+. ./test-lib.sh
+. ../diff-lib.sh
+
+# Ray Lehtiniemi's example
+
+cat << EOF > x
+do {
+   nothing;
+} while (0);
+EOF
+
+git update-index --add x
+
+cat << EOF > x
+do
+{
+   nothing;
+}
+while (0);
+EOF
+
+cat << EOF > expect
+diff --git a/x b/x
+index adf3937..6edc172 100644
+--- a/x
++++ b/x
+@@ -1,3 +1,5 @@
+-do {
++do
++{
+    nothing;
+-} while (0);
++}
++while (0);
+EOF
+
+git diff > out
+test_expect_success "Ray's example without options" 'test_cmp expect out'
+
+git diff -w > out
+test_expect_success "Ray's example with -w" 'test_cmp expect out'
+
+git diff -b > out
+test_expect_success "Ray's example with -b" 'test_cmp expect out'
+
+tr 'Q' '\015' << EOF > x
+whitespace at beginning
+whitespace change
+whitespace in the middle
+whitespace at end
+unchanged line
+CR at endQ
+EOF
+
+git update-index x
+
+tr '_' ' ' << EOF > x
+                whitespace at beginning
+whitespace          change
+white space in the middle
+whitespace at end__
+unchanged line
+CR at end
+EOF
+
+tr 'Q_' '\015 ' << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+--- a/x
++++ b/x
+@@ -1,6 +1,6 @@
+-whitespace at beginning
+-whitespace change
+-whitespace in the middle
+-whitespace at end
++             whitespace at beginning
++whitespace       change
++white space in the middle
++whitespace at end__
+unchanged line
+-CR at endQ
++CR at end
+EOF
+git diff > out
+test_expect_success 'another test, without options' 'test_cmp expect out'
+
+cat << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+EOF
+git diff -w > out
+test_expect_success 'another test, with -w' 'test_cmp expect out'
+
+tr 'Q' '\015' << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+--- a/x
++++ b/x
+@@ -1,6 +1,6 @@
+-whitespace at beginning
++             whitespace at beginning
+whitespace change
+-whitespace in the middle
++white space in the middle
+whitespace at end
+unchanged line
+CR at endQ
+EOF
+git diff -b > out
+test_expect_success 'another test, with -b' 'test_cmp expect out'
+
+test_expect_success 'check mixed spaces and tabs in indent' '
+
+                # This is indented with SP HT SP.
+                echo "    foo();" > x &&
+                git diff --check | grep "space before tab in indent"
+
+'
+
+test_expect_success 'check mixed tabs and spaces in indent' '
+
+                # This is indented with HT SP HT.
+                echo "                  foo();" > x &&
+                git diff --check | grep "space before tab in indent"
+
+'
+
+test_expect_success 'check with no whitespace errors' '
+
+                git commit -m "snapshot" &&
+                echo "foo();" > x &&
+                git diff --check
+
+'
+
+test_expect_success 'check with trailing whitespace' '
+
+                echo "foo(); " > x &&
+                test_must_fail git diff --check
+
+'
+
+test_expect_success 'check with space before tab in indent' '
+
+                # indent has space followed by hard tab
+                echo "   foo();" > x &&
+                test_must_fail git diff --check
+
+'
+
+test_expect_success '--check and --exit-code are not exclusive' '
+
+                git checkout x &&
+                git diff --check --exit-code
+
+'
+
+test_expect_success '--check and --quiet are not exclusive' '
+
+                git diff --check --quiet
+
+'
+
+test_expect_success 'check staged with no whitespace errors' '
+
+                echo "foo();" > x &&
+                git add x &&
+                git diff --cached --check
+
+'
+
+test_expect_success 'check staged with trailing whitespace' '
+
+                echo "foo(); " > x &&
+                git add x &&
+                test_must_fail git diff --cached --check
+
+'
+
+test_expect_success 'check staged with space before tab in indent' '
+
+                # indent has space followed by hard tab
+                echo "   foo();" > x &&
+                git add x &&
+                test_must_fail git diff --cached --check
+
+'
+
+test_expect_success 'check with no whitespace errors (diff-index)' '
+
+                echo "foo();" > x &&
+                git add x &&
+                git diff-index --check HEAD
+
+'
+
+test_expect_success 'check with trailing whitespace (diff-index)' '
+
+                echo "foo(); " > x &&
+                git add x &&
+                test_must_fail git diff-index --check HEAD
+
+'
+
+test_expect_success 'check with space before tab in indent (diff-index)' '
+
+                # indent has space followed by hard tab
+                echo "   foo();" > x &&
+                git add x &&
+                test_must_fail git diff-index --check HEAD
+
+'
+
+test_expect_success 'check staged with no whitespace errors (diff-index)' '
+
+                echo "foo();" > x &&
+                git add x &&
+                git diff-index --cached --check HEAD
+
+'
+
+test_expect_success 'check staged with trailing whitespace (diff-index)' '
+
+                echo "foo(); " > x &&
+                git add x &&
+                test_must_fail git diff-index --cached --check HEAD
+
+'
+
+test_expect_success 'check staged with space before tab in indent (diff-index)' '
+
+                # indent has space followed by hard tab
+                echo "   foo();" > x &&
+                git add x &&
+                test_must_fail git diff-index --cached --check HEAD
+
+'
+
+test_expect_success 'check with no whitespace errors (diff-tree)' '
+
+                echo "foo();" > x &&
+                git commit -m "new commit" x &&
+                git diff-tree --check HEAD^ HEAD
+
+'
+
+test_expect_success 'check with trailing whitespace (diff-tree)' '
+
+                echo "foo(); " > x &&
+                git commit -m "another commit" x &&
+                test_must_fail git diff-tree --check HEAD^ HEAD
+
+'
+
+test_expect_success 'check with space before tab in indent (diff-tree)' '
+
+                # indent has space followed by hard tab
+                echo "   foo();" > x &&
+                git commit -m "yet another" x &&
+                test_must_fail git diff-tree --check HEAD^ HEAD
+
+'
+
+test_expect_success 'check trailing whitespace (trailing-space: off)' '
+
+                git config core.whitespace "-trailing-space" &&
+                echo "foo ();   " > x &&
+                git diff --check
+
+'
+
+test_expect_success 'check trailing whitespace (trailing-space: on)' '
+
+                git config core.whitespace "trailing-space" &&
+                echo "foo ();   " > x &&
+                test_must_fail git diff --check
+
+'
+
+test_expect_success 'check space before tab in indent (space-before-tab: off)' '
+
+                # indent contains space followed by HT
+                git config core.whitespace "-space-before-tab" &&
+                echo "   foo ();" > x &&
+                git diff --check
+
+'
+
+test_expect_success 'check space before tab in indent (space-before-tab: on)' '
+
+                # indent contains space followed by HT
+                git config core.whitespace "space-before-tab" &&
+                echo "   foo ();   " > x &&
+                test_must_fail git diff --check
+
+'
+
+test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '
+
+                git config core.whitespace "-indent-with-non-tab"
+                echo "        foo ();" > x &&
+                git diff --check
+
+'
+
+test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '
+
+                git config core.whitespace "indent-with-non-tab" &&
+                echo "        foo ();" > x &&
+                test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '
+
+                git config core.whitespace "indent-with-non-tab" &&
+                echo "                   foo ();" > x &&
+                test_must_fail git diff --check
+
+'
+
+test_expect_success 'line numbers in --check output are correct' '
+
+                echo "" > x &&
+                echo "foo(); " >> x &&
+                git diff --check | grep "x:2:"
+
+'
+
+test_expect_success 'checkdiff detects trailing blank lines' '
+                echo "foo();" >x &&
+                echo "" >>x &&
+                git diff --check | grep "ends with blank"
+'
+
+test_expect_success 'checkdiff allows new blank lines' '
+                git checkout x &&
+                mv x y &&
+                (
+                                echo "/* This is new */" &&
+                                echo "" &&
+                                cat y
+                ) >x &&
+                git diff --check
+'
+
+test_expect_success 'combined diff with autocrlf conversion' '
+
+                git reset --hard &&
+                echo >x hello &&
+                git commit -m "one side" x &&
+                git checkout HEAD^ &&
+                echo >x goodbye &&
+                git commit -m "the other side" x &&
+                git config core.autocrlf true &&
+                test_must_fail git merge master &&
+
+                git diff | sed -e "1,/^@@@/d" >actual &&
+                ! grep "^-" actual
+
+'
+
+test_done
+
diff --git a/src/test/resources/mocks/5B.txt b/java-diff-utils/src/test/resources/mocks/5B.txt
similarity index 96%
rename from src/test/resources/mocks/5B.txt
rename to java-diff-utils/src/test/resources/mocks/5B.txt
index dd12cf4f7db49f8224556b693b4de32a89a5bfbf..112cff5af638f24044b54805cd94aff7fd8082e8 100644
--- a/src/test/resources/mocks/5B.txt
+++ b/java-diff-utils/src/test/resources/mocks/5B.txt
@@ -1,381 +1,381 @@
-#!/bin/sh
-#
-# Copyright (c) 2006 Johannes E. Schindelin
-#
-
-test_description='Test special whitespace in diff engine.
-
-'
-. ./test-lib.sh
-. ../diff-lib.sh
-
-# Ray Lehtiniemi's example
-
-cat << EOF > x
-do {
-   nothing;
-} while (0);
-EOF
-
-git update-index --add x
-
-cat << EOF > x
-do
-{
-   nothing;
-}
-while (0);
-EOF
-
-cat << EOF > expect
-diff --git a/x b/x
-index adf3937..6edc172 100644
---- a/x
-+++ b/x
-@@ -1,3 +1,5 @@
--do {
-+do
-+{
-    nothing;
--} while (0);
-+}
-+while (0);
-EOF
-
-git diff > out
-test_expect_success "Ray's example without options" 'test_cmp expect out'
-
-git diff -w > out
-test_expect_success "Ray's example with -w" 'test_cmp expect out'
-
-git diff -b > out
-test_expect_success "Ray's example with -b" 'test_cmp expect out'
-
-tr 'Q' '\015' << EOF > x
-whitespace at beginning
-whitespace change
-whitespace in the middle
-whitespace at end
-unchanged line
-CR at endQ
-EOF
-
-git update-index x
-
-tr '_' ' ' << EOF > x
-                whitespace at beginning
-whitespace          change
-white space in the middle
-whitespace at end__
-unchanged line
-CR at end
-EOF
-
-tr 'Q_' '\015 ' << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
---- a/x
-+++ b/x
-@@ -1,6 +1,6 @@
--whitespace at beginning
--whitespace change
--whitespace in the middle
--whitespace at end
-+             whitespace at beginning
-+whitespace       change
-+white space in the middle
-+whitespace at end__
-unchanged line
--CR at endQ
-+CR at end
-EOF
-git diff > out
-test_expect_success 'another test, without options' 'test_cmp expect out'
-
-cat << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
-EOF
-git diff -w > out
-test_expect_success 'another test, with -w' 'test_cmp expect out'
-
-tr 'Q' '\015' << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
---- a/x
-+++ b/x
-@@ -1,6 +1,6 @@
--whitespace at beginning
-+             whitespace at beginning
-whitespace change
--whitespace in the middle
-+white space in the middle
-whitespace at end
-unchanged line
-CR at endQ
-git diff -b --ignore-space-at-eol > out
-test_expect_failure 'another test, with -b --ignore-space-at-eol' 'test_cmp expect out'
-
-tr 'Q' '\015' << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
---- a/x
-+++ b/x
-EOF
-git diff -b > out
-test_expect_success 'another test, with -b' 'test_cmp expect out'
-
-test_expect_success 'check mixed spaces and tabs in indent' '
-
-                # This is indented with SP HT SP.
-                echo "    foo();" > x &&
-                git diff --check | grep "space before tab in indent"
-
-'
-
-test_expect_success 'check mixed tabs and spaces in indent' '
-
-                # This is indented with HT SP HT.
-                echo "                  foo();" > x &&
-                git diff --check | grep "space before tab in indent"
-
-'
-
-test_expect_success 'check with no whitespace errors' '
-
-                git commit -m "snapshot" &&
-                echo "foo();" > x &&
-                git diff --check
-
-'
-
-test_expect_success 'check with trailing whitespace' '
-
-                echo "foo(); " > x &&
-                test_must_fail git diff --check
-
-'
-
-test_expect_success 'check with space before tab in indent' '
-
-                # indent has space followed by hard tab
-                echo "   foo();" > x &&
-                test_must_fail git diff --check
-
-'
-
-test_expect_success '--check and --exit-code are not exclusive' '
-
-                git checkout x &&
-                git diff --check --exit-code
-
-'
-
-test_expect_success '--check and --quiet are not exclusive' '
-
-                git diff --check --quiet
-
-'
-
-test_expect_success 'check staged with no whitespace errors' '
-
-                echo "foo();" > x &&
-                git add x &&
-                git diff --cached --check
-
-'
-
-test_expect_success 'check staged with trailing whitespace' '
-
-                echo "foo(); " > x &&
-                git add x &&
-                test_must_fail git diff --cached --check
-
-'
-
-test_expect_success 'check staged with space before tab in indent' '
-
-                # indent has space followed by hard tab
-                echo "   foo();" > x &&
-                git add x &&
-                test_must_fail git diff --cached --check
-
-'
-
-test_expect_success 'check with no whitespace errors (diff-index)' '
-
-                echo "foo();" > x &&
-                git add x &&
-                git diff-index --check HEAD
-
-'
-
-test_expect_success 'check with trailing whitespace (diff-index)' '
-
-                echo "foo(); " > x &&
-                git add x &&
-                test_must_fail git diff-index --check HEAD
-
-'
-
-test_expect_success 'check with space before tab in indent (diff-index)' '
-
-                # indent has space followed by hard tab
-                echo "   foo();" > x &&
-                git add x &&
-                test_must_fail git diff-index --check HEAD
-
-'
-
-test_expect_success 'check staged with no whitespace errors (diff-index)' '
-
-                echo "foo();" > x &&
-                git add x &&
-                git diff-index --cached --check HEAD
-
-'
-
-test_expect_success 'check staged with trailing whitespace (diff-index)' '
-
-                echo "foo(); " > x &&
-                git add x &&
-                test_must_fail git diff-index --cached --check HEAD
-
-'
-
-test_expect_success 'check staged with space before tab in indent (diff-index)' '
-
-                # indent has space followed by hard tab
-                echo "   foo();" > x &&
-                git add x &&
-                test_must_fail git diff-index --cached --check HEAD
-
-'
-
-test_expect_success 'check with no whitespace errors (diff-tree)' '
-
-                echo "foo();" > x &&
-                git commit -m "new commit" x &&
-                git diff-tree --check HEAD^ HEAD
-
-'
-
-test_expect_success 'check with trailing whitespace (diff-tree)' '
-
-                echo "foo(); " > x &&
-                git commit -m "another commit" x &&
-                test_must_fail git diff-tree --check HEAD^ HEAD
-
-'
-
-test_expect_success 'check with space before tab in indent (diff-tree)' '
-
-                # indent has space followed by hard tab
-                echo "   foo();" > x &&
-                git commit -m "yet another" x &&
-                test_must_fail git diff-tree --check HEAD^ HEAD
-
-'
-
-test_expect_success 'check trailing whitespace (trailing-space: off)' '
-
-                git config core.whitespace "-trailing-space" &&
-                echo "foo ();   " > x &&
-                git diff --check
-
-'
-
-test_expect_success 'check trailing whitespace (trailing-space: on)' '
-
-                git config core.whitespace "trailing-space" &&
-                echo "foo ();   " > x &&
-                test_must_fail git diff --check
-
-'
-
-test_expect_success 'check space before tab in indent (space-before-tab: off)' '
-
-                # indent contains space followed by HT
-                git config core.whitespace "-space-before-tab" &&
-                echo "   foo ();" > x &&
-                git diff --check
-
-'
-
-test_expect_success 'check space before tab in indent (space-before-tab: on)' '
-
-                # indent contains space followed by HT
-                git config core.whitespace "space-before-tab" &&
-                echo "   foo ();   " > x &&
-                test_must_fail git diff --check
-
-'
-
-test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '
-
-                git config core.whitespace "-indent-with-non-tab"
-                echo "        foo ();" > x &&
-                git diff --check
-
-'
-
-test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '
-
-                git config core.whitespace "indent-with-non-tab" &&
-                echo "        foo ();" > x &&
-                test_must_fail git diff --check
-
-'
-
-test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '
-
-                git config core.whitespace "indent-with-non-tab" &&
-                echo "                   foo ();" > x &&
-                test_must_fail git diff --check
-
-'
-
-test_expect_success 'line numbers in --check output are correct' '
-
-                echo "" > x &&
-                echo "foo(); " >> x &&
-                git diff --check | grep "x:2:"
-
-'
-
-test_expect_success 'checkdiff detects trailing blank lines' '
-                echo "foo();" >x &&
-                echo "" >>x &&
-                git diff --check | grep "ends with blank"
-'
-
-test_expect_success 'checkdiff allows new blank lines' '
-                git checkout x &&
-                mv x y &&
-                (
-                                echo "/* This is new */" &&
-                                echo "" &&
-                                cat y
-                ) >x &&
-                git diff --check
-'
-
-test_expect_success 'combined diff with autocrlf conversion' '
-
-                git reset --hard &&
-                echo >x hello &&
-                git commit -m "one side" x &&
-                git checkout HEAD^ &&
-                echo >x goodbye &&
-                git commit -m "the other side" x &&
-                git config core.autocrlf true &&
-                test_must_fail git merge master &&
-
-                git diff | sed -e "1,/^@@@/d" >actual &&
-                ! grep "^-" actual
-
-'
-
-test_done
-
-
+#!/bin/sh
+#
+# Copyright (c) 2006 Johannes E. Schindelin
+#
+
+test_description='Test special whitespace in diff engine.
+
+'
+. ./test-lib.sh
+. ../diff-lib.sh
+
+# Ray Lehtiniemi's example
+
+cat << EOF > x
+do {
+   nothing;
+} while (0);
+EOF
+
+git update-index --add x
+
+cat << EOF > x
+do
+{
+   nothing;
+}
+while (0);
+EOF
+
+cat << EOF > expect
+diff --git a/x b/x
+index adf3937..6edc172 100644
+--- a/x
++++ b/x
+@@ -1,3 +1,5 @@
+-do {
++do
++{
+    nothing;
+-} while (0);
++}
++while (0);
+EOF
+
+git diff > out
+test_expect_success "Ray's example without options" 'test_cmp expect out'
+
+git diff -w > out
+test_expect_success "Ray's example with -w" 'test_cmp expect out'
+
+git diff -b > out
+test_expect_success "Ray's example with -b" 'test_cmp expect out'
+
+tr 'Q' '\015' << EOF > x
+whitespace at beginning
+whitespace change
+whitespace in the middle
+whitespace at end
+unchanged line
+CR at endQ
+EOF
+
+git update-index x
+
+tr '_' ' ' << EOF > x
+                whitespace at beginning
+whitespace          change
+white space in the middle
+whitespace at end__
+unchanged line
+CR at end
+EOF
+
+tr 'Q_' '\015 ' << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+--- a/x
++++ b/x
+@@ -1,6 +1,6 @@
+-whitespace at beginning
+-whitespace change
+-whitespace in the middle
+-whitespace at end
++             whitespace at beginning
++whitespace       change
++white space in the middle
++whitespace at end__
+unchanged line
+-CR at endQ
++CR at end
+EOF
+git diff > out
+test_expect_success 'another test, without options' 'test_cmp expect out'
+
+cat << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+EOF
+git diff -w > out
+test_expect_success 'another test, with -w' 'test_cmp expect out'
+
+tr 'Q' '\015' << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+--- a/x
++++ b/x
+@@ -1,6 +1,6 @@
+-whitespace at beginning
++             whitespace at beginning
+whitespace change
+-whitespace in the middle
++white space in the middle
+whitespace at end
+unchanged line
+CR at endQ
+git diff -b --ignore-space-at-eol > out
+test_expect_failure 'another test, with -b --ignore-space-at-eol' 'test_cmp expect out'
+
+tr 'Q' '\015' << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+--- a/x
++++ b/x
+EOF
+git diff -b > out
+test_expect_success 'another test, with -b' 'test_cmp expect out'
+
+test_expect_success 'check mixed spaces and tabs in indent' '
+
+                # This is indented with SP HT SP.
+                echo "    foo();" > x &&
+                git diff --check | grep "space before tab in indent"
+
+'
+
+test_expect_success 'check mixed tabs and spaces in indent' '
+
+                # This is indented with HT SP HT.
+                echo "                  foo();" > x &&
+                git diff --check | grep "space before tab in indent"
+
+'
+
+test_expect_success 'check with no whitespace errors' '
+
+                git commit -m "snapshot" &&
+                echo "foo();" > x &&
+                git diff --check
+
+'
+
+test_expect_success 'check with trailing whitespace' '
+
+                echo "foo(); " > x &&
+                test_must_fail git diff --check
+
+'
+
+test_expect_success 'check with space before tab in indent' '
+
+                # indent has space followed by hard tab
+                echo "   foo();" > x &&
+                test_must_fail git diff --check
+
+'
+
+test_expect_success '--check and --exit-code are not exclusive' '
+
+                git checkout x &&
+                git diff --check --exit-code
+
+'
+
+test_expect_success '--check and --quiet are not exclusive' '
+
+                git diff --check --quiet
+
+'
+
+test_expect_success 'check staged with no whitespace errors' '
+
+                echo "foo();" > x &&
+                git add x &&
+                git diff --cached --check
+
+'
+
+test_expect_success 'check staged with trailing whitespace' '
+
+                echo "foo(); " > x &&
+                git add x &&
+                test_must_fail git diff --cached --check
+
+'
+
+test_expect_success 'check staged with space before tab in indent' '
+
+                # indent has space followed by hard tab
+                echo "   foo();" > x &&
+                git add x &&
+                test_must_fail git diff --cached --check
+
+'
+
+test_expect_success 'check with no whitespace errors (diff-index)' '
+
+                echo "foo();" > x &&
+                git add x &&
+                git diff-index --check HEAD
+
+'
+
+test_expect_success 'check with trailing whitespace (diff-index)' '
+
+                echo "foo(); " > x &&
+                git add x &&
+                test_must_fail git diff-index --check HEAD
+
+'
+
+test_expect_success 'check with space before tab in indent (diff-index)' '
+
+                # indent has space followed by hard tab
+                echo "   foo();" > x &&
+                git add x &&
+                test_must_fail git diff-index --check HEAD
+
+'
+
+test_expect_success 'check staged with no whitespace errors (diff-index)' '
+
+                echo "foo();" > x &&
+                git add x &&
+                git diff-index --cached --check HEAD
+
+'
+
+test_expect_success 'check staged with trailing whitespace (diff-index)' '
+
+                echo "foo(); " > x &&
+                git add x &&
+                test_must_fail git diff-index --cached --check HEAD
+
+'
+
+test_expect_success 'check staged with space before tab in indent (diff-index)' '
+
+                # indent has space followed by hard tab
+                echo "   foo();" > x &&
+                git add x &&
+                test_must_fail git diff-index --cached --check HEAD
+
+'
+
+test_expect_success 'check with no whitespace errors (diff-tree)' '
+
+                echo "foo();" > x &&
+                git commit -m "new commit" x &&
+                git diff-tree --check HEAD^ HEAD
+
+'
+
+test_expect_success 'check with trailing whitespace (diff-tree)' '
+
+                echo "foo(); " > x &&
+                git commit -m "another commit" x &&
+                test_must_fail git diff-tree --check HEAD^ HEAD
+
+'
+
+test_expect_success 'check with space before tab in indent (diff-tree)' '
+
+                # indent has space followed by hard tab
+                echo "   foo();" > x &&
+                git commit -m "yet another" x &&
+                test_must_fail git diff-tree --check HEAD^ HEAD
+
+'
+
+test_expect_success 'check trailing whitespace (trailing-space: off)' '
+
+                git config core.whitespace "-trailing-space" &&
+                echo "foo ();   " > x &&
+                git diff --check
+
+'
+
+test_expect_success 'check trailing whitespace (trailing-space: on)' '
+
+                git config core.whitespace "trailing-space" &&
+                echo "foo ();   " > x &&
+                test_must_fail git diff --check
+
+'
+
+test_expect_success 'check space before tab in indent (space-before-tab: off)' '
+
+                # indent contains space followed by HT
+                git config core.whitespace "-space-before-tab" &&
+                echo "   foo ();" > x &&
+                git diff --check
+
+'
+
+test_expect_success 'check space before tab in indent (space-before-tab: on)' '
+
+                # indent contains space followed by HT
+                git config core.whitespace "space-before-tab" &&
+                echo "   foo ();   " > x &&
+                test_must_fail git diff --check
+
+'
+
+test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '
+
+                git config core.whitespace "-indent-with-non-tab"
+                echo "        foo ();" > x &&
+                git diff --check
+
+'
+
+test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '
+
+                git config core.whitespace "indent-with-non-tab" &&
+                echo "        foo ();" > x &&
+                test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '
+
+                git config core.whitespace "indent-with-non-tab" &&
+                echo "                   foo ();" > x &&
+                test_must_fail git diff --check
+
+'
+
+test_expect_success 'line numbers in --check output are correct' '
+
+                echo "" > x &&
+                echo "foo(); " >> x &&
+                git diff --check | grep "x:2:"
+
+'
+
+test_expect_success 'checkdiff detects trailing blank lines' '
+                echo "foo();" >x &&
+                echo "" >>x &&
+                git diff --check | grep "ends with blank"
+'
+
+test_expect_success 'checkdiff allows new blank lines' '
+                git checkout x &&
+                mv x y &&
+                (
+                                echo "/* This is new */" &&
+                                echo "" &&
+                                cat y
+                ) >x &&
+                git diff --check
+'
+
+test_expect_success 'combined diff with autocrlf conversion' '
+
+                git reset --hard &&
+                echo >x hello &&
+                git commit -m "one side" x &&
+                git checkout HEAD^ &&
+                echo >x goodbye &&
+                git commit -m "the other side" x &&
+                git config core.autocrlf true &&
+                test_must_fail git merge master &&
+
+                git diff | sed -e "1,/^@@@/d" >actual &&
+                ! grep "^-" actual
+
+'
+
+test_done
+
+
diff --git a/src/test/resources/mocks/issue10_base.txt b/java-diff-utils/src/test/resources/mocks/issue10_base.txt
similarity index 100%
rename from src/test/resources/mocks/issue10_base.txt
rename to java-diff-utils/src/test/resources/mocks/issue10_base.txt
diff --git a/src/test/resources/mocks/issue10_patch.txt b/java-diff-utils/src/test/resources/mocks/issue10_patch.txt
similarity index 100%
rename from src/test/resources/mocks/issue10_patch.txt
rename to java-diff-utils/src/test/resources/mocks/issue10_patch.txt
diff --git a/src/test/resources/mocks/issue11_1.txt b/java-diff-utils/src/test/resources/mocks/issue11_1.txt
similarity index 100%
rename from src/test/resources/mocks/issue11_1.txt
rename to java-diff-utils/src/test/resources/mocks/issue11_1.txt
diff --git a/src/test/resources/mocks/issue11_2.txt b/java-diff-utils/src/test/resources/mocks/issue11_2.txt
similarity index 100%
rename from src/test/resources/mocks/issue11_2.txt
rename to java-diff-utils/src/test/resources/mocks/issue11_2.txt
diff --git a/src/test/resources/mocks/issue15_1.txt b/java-diff-utils/src/test/resources/mocks/issue15_1.txt
similarity index 100%
rename from src/test/resources/mocks/issue15_1.txt
rename to java-diff-utils/src/test/resources/mocks/issue15_1.txt
diff --git a/src/test/resources/mocks/issue15_2.txt b/java-diff-utils/src/test/resources/mocks/issue15_2.txt
similarity index 100%
rename from src/test/resources/mocks/issue15_2.txt
rename to java-diff-utils/src/test/resources/mocks/issue15_2.txt
diff --git a/src/test/resources/mocks/one_delta_test_original.txt b/java-diff-utils/src/test/resources/mocks/one_delta_test_original.txt
similarity index 100%
rename from src/test/resources/mocks/one_delta_test_original.txt
rename to java-diff-utils/src/test/resources/mocks/one_delta_test_original.txt
diff --git a/src/test/resources/mocks/one_delta_test_revised.txt b/java-diff-utils/src/test/resources/mocks/one_delta_test_revised.txt
similarity index 100%
rename from src/test/resources/mocks/one_delta_test_revised.txt
rename to java-diff-utils/src/test/resources/mocks/one_delta_test_revised.txt
diff --git a/src/test/resources/mocks/original.txt b/java-diff-utils/src/test/resources/mocks/original.txt
similarity index 100%
rename from src/test/resources/mocks/original.txt
rename to java-diff-utils/src/test/resources/mocks/original.txt
diff --git a/src/test/resources/mocks/revised.txt b/java-diff-utils/src/test/resources/mocks/revised.txt
similarity index 100%
rename from src/test/resources/mocks/revised.txt
rename to java-diff-utils/src/test/resources/mocks/revised.txt
diff --git a/pom.xml b/pom.xml
index 5a85f49f48ec9346654e761bc375a0e657028b56..467e46b8987f02d2c39d7674c6d8f8381bfa7da2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,10 +1,15 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <groupId>io.github.java-diff-utils</groupId>
-    <artifactId>java-diff-utils</artifactId>
-    <packaging>jar</packaging>
-    <version>4.0</version>
-    <name>java-diff-utils</name>
+    <artifactId>java-diff-utils-parent</artifactId>
+    <version>4.4</version>
+    <name>java-diff-utils-parent</name>
+    <packaging>pom</packaging>
+    <modules>
+        <module>java-diff-utils</module>
+        <module>java-diff-utils-jgit</module>
+    </modules>
     <description>The DiffUtils library for computing diffs, applying patches, generationg side-by-side view in Java.</description>
     <url>https://github.com/java-diff-utils/java-diff-utils</url>
     <inceptionYear>2009</inceptionYear>
@@ -24,9 +29,8 @@
         <connection>scm:git:https://github.com/java-diff-utils/java-diff-utils.git</connection>
         <developerConnection>scm:git:ssh://git@github.com:java-diff-utils/java-diff-utils.git</developerConnection>
         <url>https://github.com/java-diff-utils/java-diff-utils.git</url>
-        <tag>java-diff-utils-4.0</tag>
+        <tag>java-diff-utils-parent-4.4</tag>
     </scm>
-	
     <issueManagement>
         <system>GitHub Issues</system>
         <url>https://github.com/java-diff-utils/java-diff-utils/issues</url>
@@ -41,16 +45,6 @@
             <name>Tobias Warneke</name>
             <email>t.warneke@gmx.net</email>
         </developer>
-        <!-- 
-            <developer>
-                <name>Dmitry Naumenko</name>
-                <email>dm.naumenko@gmail.com</email>
-            </developer>
-            <developer>
-                <name>Juanco Anez</name>
-                <email>juanco@suigeneris.org</email>
-            </developer>
-        -->
     </developers>
 	
     <licenses>
@@ -61,81 +55,21 @@
             <comments>A business-friendly OSS license</comments>
         </license>
     </licenses>
-
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
     </properties>
-
-    <dependencies>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.12</version>
-            <type>jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jgit</groupId>
-            <artifactId>org.eclipse.jgit</artifactId>
-            <version>4.4.1.201607150455-r</version>
-            <exclusions>
-                <exclusion>
-                    <groupId>com.googlecode.javaewah</groupId>
-                    <artifactId>JavaEWAH</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>commons-codec</groupId>
-                    <artifactId>commons-codec</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>commons-logging</groupId>
-                    <artifactId>commons-logging</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.apache.httpcomponents</groupId>
-                    <artifactId>httpclient</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>com.jcraft</groupId>
-                    <artifactId>jsch</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.slf4j</groupId>
-                    <artifactId>slf4j-api</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-    </dependencies>
-
     <build>
         <plugins>
-			
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.6.1</version>
-                <configuration>
-                    <source>1.8</source>
-                    <target>1.8</target>
-                    <encoding>UTF-8</encoding>
-                </configuration>
-            </plugin>
-			
-            <!-- Make this JAR OSGi ready -->
-            <!-- We want to keep packaging type as jar. Therefore we need to customize the MANIFEST.MF. 
-            See http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html
-            -->
-            <plugin>
-                <artifactId>maven-jar-plugin</artifactId>
-                <version>3.0.2</version>
+                <artifactId>maven-release-plugin</artifactId>
+                <version>2.5.3</version>
                 <configuration>
-                    <archive>
-                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
-                        <manifestEntries>
-                            <!-- identical to OSGI name -->
-                            <Automatic-Module-Name>com.github.wumpz.diffutils</Automatic-Module-Name>
-                        </manifestEntries>
-                    </archive>
+                    <localCheckout>true</localCheckout>
+                    <pushChanges>false</pushChanges>
+                    <mavenExecutorId>forked-path</mavenExecutorId>
                 </configuration>
             </plugin>
             <plugin>
@@ -168,26 +102,6 @@
                     </execution>
                 </executions>
             </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <version>2.19.1</version>
-                <configuration>
-                    <excludes>
-                        <exclude>**/LR*.java</exclude>
-                    </excludes>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-release-plugin</artifactId>
-                <version>2.5.3</version>
-                <configuration>
-                    <localCheckout>true</localCheckout>
-                    <pushChanges>false</pushChanges>
-                    <mavenExecutorId>forked-path</mavenExecutorId>
-                </configuration>
-            </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-checkstyle-plugin</artifactId>
@@ -209,12 +123,6 @@
                         <module name="Checker">
                             <module name="SuppressWarningsFilter" />
                             <module name="FileTabCharacter" />
-                            <!-- git checkout may change linefeeds on the fly
-                            <module name="RegexpMultiline">
-                                <property name="format" value="(?s:(\r\n|\r).*)" />
-                                <property name="message" value="CRLF and CR line endings are prohibited, but this file uses them." />
-                            </module>
-                            -->
                             <module name="TreeWalker">
                                 <module name="AvoidNestedBlocks" />
                                 <module name="ConstantName" />
@@ -243,10 +151,20 @@
                     <dependency>
                         <groupId>com.puppycrawl.tools</groupId>
                         <artifactId>checkstyle</artifactId>
-                        <version>6.19</version>
+                        <version>8.18</version>
                     </dependency>
                 </dependencies>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.22.1</version>
+                <configuration>
+                    <excludes>
+                        <exclude>**/LR*.java</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
     <profiles>
@@ -306,5 +224,4 @@
             </build>
         </profile>
     </profiles>
-</project>
-
+</project>
\ No newline at end of file