You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by mi...@apache.org on 2020/10/16 13:02:29 UTC

[maven-enforcer] branch MENFORCER-361 created (now 2babf83)

This is an automated email from the ASF dual-hosted git repository.

michaelo pushed a change to branch MENFORCER-361
in repository https://gitbox.apache.org/repos/asf/maven-enforcer.git.


      at 2babf83  [MENFORCER-361] Introduce RequireTextFileChecksum with line separator normalization

This branch includes the following new commits:

     new 2babf83  [MENFORCER-361] Introduce RequireTextFileChecksum with line separator normalization

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[maven-enforcer] 01/01: [MENFORCER-361] Introduce RequireTextFileChecksum with line separator normalization

Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

michaelo pushed a commit to branch MENFORCER-361
in repository https://gitbox.apache.org/repos/asf/maven-enforcer.git

commit 2babf83f8cb03b8565fa48fe429dbdd8f9fccee4
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Sun Sep 13 17:32:24 2020 +0200

    [MENFORCER-361] Introduce RequireTextFileChecksum with line separator normalization
    
    This closes #79
---
 enforcer-rules/pom.xml                             |   4 +
 .../plugins/enforcer/RequireFileChecksum.java      |  65 ++++----
 .../plugins/enforcer/RequireTextFileChecksum.java  | 102 ++++++++++++
 .../utils/NormalizeLineSeparatorReader.java        | 177 +++++++++++++++++++++
 .../src/site/apt/requireFileChecksum.apt.vm        |   7 +-
 ...cksum.apt.vm => requireTextFileChecksum.apt.vm} |  44 +++--
 .../plugins/enforcer/TestRequireFileChecksum.java  |   2 +
 .../enforcer/TestRequireTextFileChecksum.java      | 128 +++++++++++++++
 .../utils/TestNormalizeLineSeparatorReader.java    |  77 +++++++++
 pom.xml                                            |   7 +-
 10 files changed, 564 insertions(+), 49 deletions(-)

diff --git a/enforcer-rules/pom.xml b/enforcer-rules/pom.xml
index 03077da..3fb4426 100644
--- a/enforcer-rules/pom.xml
+++ b/enforcer-rules/pom.xml
@@ -67,6 +67,10 @@
       <artifactId>commons-codec</artifactId>
     </dependency>
     <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+    </dependency>
+    <dependency>
       <groupId>org.apache.maven.enforcer</groupId>
       <artifactId>enforcer-api</artifactId>
     </dependency>
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFileChecksum.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFileChecksum.java
index bbed9e7..774a493 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFileChecksum.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFileChecksum.java
@@ -29,16 +29,17 @@ import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
 import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
 
 /**
- * Rule to validate a file to match the specified checksum.
+ * Rule to validate a binary file to match the specified checksum.
  *
  * @author Edward Samson
  * @author Lyubomyr Shaydariv
+ * @see RequireTextFileChecksum
  */
 public class RequireFileChecksum
     extends AbstractNonCacheableEnforcerRule
 {
 
-    private File file;
+    protected File file;
 
     private String checksum;
 
@@ -140,41 +141,47 @@ public class RequireFileChecksum
         this.nonexistentFileMessage = nonexistentFileMessage;
     }
 
-    private String calculateChecksum()
+    protected String calculateChecksum()
         throws EnforcerRuleException
     {
         try ( InputStream inputStream = new FileInputStream( this.file ) )
         {
-            String checksum;
-            if ( "md5".equals( this.type ) )
-            {
-                checksum = DigestUtils.md5Hex( inputStream );
-            }
-            else if ( "sha1".equals( this.type ) )
-            {
-                checksum = DigestUtils.shaHex( inputStream );
-            }
-            else if ( "sha256".equals( this.type ) )
-            {
-                checksum = DigestUtils.sha256Hex( inputStream );
-            }
-            else if ( "sha384".equals( this.type ) )
-            {
-                checksum = DigestUtils.sha384Hex( inputStream );
-            }
-            else if ( "sha512".equals( this.type ) )
-            {
-                checksum = DigestUtils.sha512Hex( inputStream );
-            }
-            else
-            {
-                throw new EnforcerRuleException( "Unsupported hash type: " + this.type );
-            }
-            return checksum;
+            return calculateChecksum( inputStream );
         }
         catch ( IOException e )
         {
             throw new EnforcerRuleException( "Unable to calculate checksum", e );
         }
     }
+
+    protected String calculateChecksum( InputStream inputStream )
+        throws IOException, EnforcerRuleException
+    {
+        String checksum;
+        if ( "md5".equals( this.type ) )
+        {
+            checksum = DigestUtils.md5Hex( inputStream );
+        }
+        else if ( "sha1".equals( this.type ) )
+        {
+            checksum = DigestUtils.shaHex( inputStream );
+        }
+        else if ( "sha256".equals( this.type ) )
+        {
+            checksum = DigestUtils.sha256Hex( inputStream );
+        }
+        else if ( "sha384".equals( this.type ) )
+        {
+            checksum = DigestUtils.sha384Hex( inputStream );
+        }
+        else if ( "sha512".equals( this.type ) )
+        {
+            checksum = DigestUtils.sha512Hex( inputStream );
+        }
+        else
+        {
+            throw new EnforcerRuleException( "Unsupported hash type: " + this.type );
+        }
+        return checksum;
+    }
 }
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireTextFileChecksum.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireTextFileChecksum.java
new file mode 100644
index 0000000..42fb8a1
--- /dev/null
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireTextFileChecksum.java
@@ -0,0 +1,102 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+
+import org.apache.commons.io.input.ReaderInputStream;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.plugins.enforcer.utils.NormalizeLineSeparatorReader;
+import org.apache.maven.plugins.enforcer.utils.NormalizeLineSeparatorReader.LineSeparator;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+
+/**
+ * Rule to validate a text file to match the specified checksum.
+ *
+ * @author Konrad Windszus
+ * @see RequireFileChecksum
+ */
+public class RequireTextFileChecksum
+    extends RequireFileChecksum
+{
+    private NormalizeLineSeparatorReader.LineSeparator normalizeLineSeparatorTo = LineSeparator.UNIX;
+
+    Charset encoding;
+
+    public void setNormalizeLineSeparatorTo( NormalizeLineSeparatorReader.LineSeparator normalizeLineSeparatorTo )
+    {
+        this.normalizeLineSeparatorTo = normalizeLineSeparatorTo;
+    }
+
+    public void setEncoding( String encoding )
+    {
+        this.encoding = Charset.forName( encoding );
+    }
+
+    @Override
+    public void execute( EnforcerRuleHelper helper )
+        throws EnforcerRuleException
+    {
+        // set defaults
+        if ( encoding == null )
+        {
+            // https://maven.apache.org/plugins/maven-resources-plugin/examples/encoding.html
+            try
+            {
+                String encoding = (String) helper.evaluate( "${project.build.sourceEncoding}" );
+                if ( StringUtils.isBlank( encoding ) )
+                {
+                    encoding = System.getProperty( "file.encoding" );
+                    helper.getLog().warn( "File encoding has not been set, using platform encoding " + encoding
+                        + ". Build is platform dependent!" );
+                }
+                this.encoding = Charset.forName( encoding );
+            }
+            catch ( ExpressionEvaluationException e )
+            {
+                throw new EnforcerRuleException( "Unable to retrieve the project's build source encoding "
+                    + "(${project.build.sourceEncoding}): ", e );
+            }
+        }
+        super.execute( helper );
+    }
+
+    @Override
+    protected String calculateChecksum()
+        throws EnforcerRuleException
+    {
+        try ( Reader reader = new NormalizeLineSeparatorReader( Files.newBufferedReader( file.toPath(), encoding ),
+                                                                normalizeLineSeparatorTo );
+                        InputStream inputStream = new ReaderInputStream( reader, encoding ) )
+        {
+            return super.calculateChecksum( inputStream );
+        }
+        catch ( IOException e )
+        {
+            throw new EnforcerRuleException( "Unable to calculate checksum (with normalized line separators)", e );
+        }
+    }
+}
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/NormalizeLineSeparatorReader.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/NormalizeLineSeparatorReader.java
new file mode 100644
index 0000000..eca36b4
--- /dev/null
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/NormalizeLineSeparatorReader.java
@@ -0,0 +1,177 @@
+package org.apache.maven.plugins.enforcer.utils;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.FilterReader;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * Converts Unix line separators to Windows ones and vice-versa.
+ */
+public class NormalizeLineSeparatorReader
+    extends FilterReader
+{
+
+    private static final int EOL = -1;
+
+    /**
+     * Type representing either Unix or Windows line separators
+     */
+    public enum LineSeparator
+    {
+        WINDOWS( "\r\n", null ), UNIX( "\n", '\r' );
+
+        private final char[] separatorChars;
+
+        private final Character notPrecededByChar;
+
+        LineSeparator( String separator, Character notPrecededByChar )
+        {
+            separatorChars = separator.toCharArray();
+            this.notPrecededByChar = notPrecededByChar;
+        }
+
+        enum MatchResult
+        {
+            NO_MATCH,
+            POTENTIAL_MATCH,
+            MATCH;
+        }
+
+        /**
+         * Checks if two given characters match the line separator represented by this object.
+         * @param currentCharacter the character to check against
+         * @param previousCharacter optional previous character (may be {@code null})
+         * @return one of {@link MatchResult}
+         */
+        public MatchResult matches( char currentCharacter, Character previousCharacter )
+        {
+            int len = separatorChars.length;
+            if ( currentCharacter == separatorChars[len - 1] )
+            {
+                if ( len > 1 )
+                {
+                    if ( previousCharacter == null || previousCharacter != separatorChars[len - 1] )
+                    {
+                        return MatchResult.NO_MATCH;
+                    }
+                }
+                if ( notPrecededByChar != null )
+                {
+                    if ( previousCharacter != null && notPrecededByChar == previousCharacter )
+                    {
+                        return MatchResult.NO_MATCH;
+                    }
+                }
+                return MatchResult.MATCH;
+            }
+            else if ( len > 1 && currentCharacter == separatorChars[len - 2] )
+            {
+                return MatchResult.POTENTIAL_MATCH;
+            }
+            return MatchResult.NO_MATCH;
+        }
+    };
+
+    final LineSeparator lineSeparator;
+
+    Character bufferedCharacter;
+
+    Character previousCharacter;
+
+    public NormalizeLineSeparatorReader( Reader reader, LineSeparator lineSeparator )
+    {
+        super( reader );
+        this.lineSeparator = lineSeparator;
+        bufferedCharacter = null;
+    }
+
+    @Override
+    public int read( char[] cbuf, int off, int len )
+        throws IOException
+    {
+        int n;
+        for ( n = off; n < off + len; n++ )
+        {
+            int readResult = read();
+            if ( readResult == EOL )
+            {
+                return n == 0 ? EOL : n;
+            }
+            else
+            {
+                cbuf[n] = (char) readResult;
+            }
+        }
+        return n;
+    }
+
+    @Override
+    public int read()
+        throws IOException
+    {
+        // spool buffered characters, if any
+        if ( bufferedCharacter != null )
+        {
+            char localBuffer = bufferedCharacter;
+            bufferedCharacter = null;
+            return localBuffer;
+        }
+        int readResult = super.read();
+        if ( readResult == EOL )
+        {
+            return readResult;
+        }
+        char currentCharacter = (char) readResult;
+        if ( lineSeparator == LineSeparator.UNIX )
+        {
+            switch ( LineSeparator.WINDOWS.matches( currentCharacter, previousCharacter ) )
+            {
+                case MATCH:
+                    return lineSeparator.separatorChars[0];
+                case POTENTIAL_MATCH:
+                    previousCharacter = currentCharacter;
+                    return read();
+                default:
+                    // fall-through
+            }
+        }
+        else
+        { // WINDOWS
+            // if current is unix, convert
+            switch ( LineSeparator.UNIX.matches( currentCharacter, previousCharacter ) )
+            {
+                case MATCH:
+                    bufferedCharacter = lineSeparator.separatorChars[1];
+                    // set buffered character and return current
+                    return lineSeparator.separatorChars[0];
+                case POTENTIAL_MATCH:
+                    // invalid option
+                    throw new IllegalStateException( "No potential matches expected for Unix line separator" );
+                default:
+                    // fall-through
+            }
+        }
+        previousCharacter = currentCharacter;
+        return currentCharacter;
+    }
+
+}
diff --git a/enforcer-rules/src/site/apt/requireFileChecksum.apt.vm b/enforcer-rules/src/site/apt/requireFileChecksum.apt.vm
index c4606cd..425f0e6 100644
--- a/enforcer-rules/src/site/apt/requireFileChecksum.apt.vm
+++ b/enforcer-rules/src/site/apt/requireFileChecksum.apt.vm
@@ -25,12 +25,15 @@
 
 Require Files Checksum
 
-  This rule checks that the specified file has an given checksum.
+  This rule checks that the specified file has an given checksum. For text files
+  use {{{./requireTextFileChecksum.html}Require Text Files Checksum}} instead.
 
 
    The following parameters are supported by this rule:
 
-   * message - an optional message to the user if the rule fails.
+   * message - an optional message to the user if the rule fails. If not set a default message will be used.
+
+   * nonexistentFileMessage - an optional message to the user if the file is missing. If not set a default message will be used.
 
    * file - A file to check.
 
diff --git a/enforcer-rules/src/site/apt/requireFileChecksum.apt.vm b/enforcer-rules/src/site/apt/requireTextFileChecksum.apt.vm
similarity index 65%
copy from enforcer-rules/src/site/apt/requireFileChecksum.apt.vm
copy to enforcer-rules/src/site/apt/requireTextFileChecksum.apt.vm
index c4606cd..c058f29 100644
--- a/enforcer-rules/src/site/apt/requireFileChecksum.apt.vm
+++ b/enforcer-rules/src/site/apt/requireTextFileChecksum.apt.vm
@@ -16,21 +16,25 @@
 ~~ under the License.
 
   ------
-  Require Files Checksum
+  Require Text Files Checksum
   ------
-  Edward Samson, Lyubomyr Shaydariv
+  Konrad Windszus
   ------
-  February 2016
+  October 2020
   ------
 
-Require Files Checksum
-
-  This rule checks that the specified file has an given checksum.
+Require Text Files Checksum
 
+  This rule checks that the specified text file has an given checksum. For binary files
+  use {{{./requireFileChecksum.html}Require Files Checksum}} instead.
+  To make sure the checksum is the same on all platforms the text file's line separators are normalized
+  to Unix (<<<\n>>>, default) or optionally Windows (<<<\r\n>>>).
 
    The following parameters are supported by this rule:
 
-   * message - an optional message to the user if the rule fails.
+   * message - an optional message to the user if the rule fails. If not set a default message will be used.
+
+   * nonexistentFileMessage - an optional message to the user if the file is missing. If not set a default message will be used.
 
    * file - A file to check.
 
@@ -38,6 +42,10 @@ Require Files Checksum
 
    * type - Type of hashing algorithm to calculate the checksum. May be one of "md5", "sha1", "sha256", "sha384", or "sha512".
 
+   * normalizeLineSeparatorTo - optionally specifies to which line separators to normalize prior to checksum calculation. Either "WINDOWS" or "UNIX". By default "UNIX".
+
+   * encoding - the character encoding used by the file. One of the {{{https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html#standard}Default Java Charset}} names. By default set to <<<$\{project.build.sourceEncoding\}>>>
+
    []
 
 
@@ -60,31 +68,33 @@ Require Files Checksum
             </goals>
             <configuration>
               <rules>
-                <requireFileChecksum>
+                <requireTextFileChecksum>
                   <file>${project.build.outputDirectory}/foo.txt</file>
                   <checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
                   <type>md5</type>
-                </requireFileChecksum>
-                <requireFileChecksum>
+                </requireTextFileChecksum>
+                <requireTextFileChecksum>
                   <file>${project.build.outputDirectory}/bar.txt</file>
                   <checksum>da39a3ee5e6b4b0d3255bfef95601890afd80709</checksum>
                   <type>sha1</type>
-                </requireFileChecksum>
-                <requireFileChecksum>
+                  <encoding>UTF-8</encoding>
+                </requireTextFileChecksum>
+                <requireTextFileChecksum>
                   <file>${project.build.outputDirectory}/baz.txt</file>
                   <checksum>e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855</checksum>
                   <type>sha256</type>
-                </requireFileChecksum>
-                <requireFileChecksum>
+                  <normalizeLineSeparatorTo>WINDOWS</normalizeLineSeparatorTo>
+                </requireTextFileChecksum>
+                <requireTextFileChecksum>
                   <file>${project.build.outputDirectory}/qux.txt</file>
                   <checksum>38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b</checksum>
                   <type>sha384</type>
-                </requireFileChecksum>
-                <requireFileChecksum>
+                </requireTextFileChecksum>
+                <requireTextFileChecksum>
                   <file>${project.build.outputDirectory}/quux.txt</file>
                   <checksum>cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e</checksum>
                   <type>sha512</type>
-                </requireFileChecksum>
+                </requireTextFileChecksum>
               </rules>
               <fail>true</fail>
             </configuration>
diff --git a/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestRequireFileChecksum.java b/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestRequireFileChecksum.java
index c7075ce..201c5aa 100644
--- a/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestRequireFileChecksum.java
+++ b/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestRequireFileChecksum.java
@@ -21,8 +21,10 @@ package org.apache.maven.plugins.enforcer;
 
 import java.io.File;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 
 import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.plugins.enforcer.utils.NormalizeLineSeparatorReader.LineSeparator;
 import org.codehaus.plexus.util.FileUtils;
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestRequireTextFileChecksum.java b/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestRequireTextFileChecksum.java
new file mode 100644
index 0000000..04fe219
--- /dev/null
+++ b/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestRequireTextFileChecksum.java
@@ -0,0 +1,128 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.plugins.enforcer.utils.NormalizeLineSeparatorReader.LineSeparator;
+import org.codehaus.plexus.util.FileUtils;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Test the "RequireTextFileChecksum" rule
+ *
+ */
+public class TestRequireTextFileChecksum
+{
+
+    private RequireTextFileChecksum rule = new RequireTextFileChecksum();
+
+    @Rule
+    public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+    @Test
+    public void testFileChecksumMd5NormalizedFromUnixToWindows()
+        throws IOException, EnforcerRuleException
+    {
+        File f = temporaryFolder.newFile();
+        FileUtils.fileWrite( f, "line1\nline2\n" );
+
+        rule.setFile( f );
+        rule.setChecksum( "c6242222cf6ccdb15a43e0e5b1a08810" );
+        rule.setType( "md5" );
+        rule.setNormalizeLineSeparatorTo( LineSeparator.WINDOWS );
+        rule.setEncoding( StandardCharsets.US_ASCII.name() );
+
+        rule.execute( EnforcerTestUtils.getHelper() );
+    }
+
+    @Test
+    public void testFileChecksumMd5NormalizedFromWindowsToWindows()
+        throws IOException, EnforcerRuleException
+    {
+        File f = temporaryFolder.newFile();
+        FileUtils.fileWrite( f, "line1\r\nline2\r\n" );
+
+        rule.setFile( f );
+        rule.setChecksum( "c6242222cf6ccdb15a43e0e5b1a08810" );
+        rule.setType( "md5" );
+        rule.setNormalizeLineSeparatorTo( LineSeparator.WINDOWS );
+        rule.setEncoding( StandardCharsets.US_ASCII.name() );
+
+        rule.execute( EnforcerTestUtils.getHelper() );
+    }
+
+    @Test
+    public void testFileChecksumMd5NormalizedFromWindowsToUnix()
+        throws IOException, EnforcerRuleException
+    {
+        File f = temporaryFolder.newFile();
+        FileUtils.fileWrite( f, "line1\r\nline2\r\n" );
+
+        rule.setFile( f );
+        rule.setChecksum( "4fcc82a88ee38e0aa16c17f512c685c9" );
+        rule.setType( "md5" );
+        rule.setNormalizeLineSeparatorTo( LineSeparator.UNIX );
+        rule.setEncoding( StandardCharsets.US_ASCII.name() );
+
+        rule.execute( EnforcerTestUtils.getHelper() );
+    }
+
+    @Test
+    public void testFileChecksumMd5NormalizedFromUnixToUnix()
+        throws IOException, EnforcerRuleException
+    {
+        File f = temporaryFolder.newFile();
+        FileUtils.fileWrite( f, "line1\nline2\n" );
+
+        rule.setFile( f );
+        rule.setChecksum( "4fcc82a88ee38e0aa16c17f512c685c9" );
+        rule.setType( "md5" );
+        rule.setNormalizeLineSeparatorTo( LineSeparator.UNIX );
+        rule.setEncoding( StandardCharsets.US_ASCII.name() );
+
+        rule.execute( EnforcerTestUtils.getHelper() );
+    }
+
+    @Test
+    public void testFileChecksumMd5NormalizedWithMissingFileCharsetParameter()
+        throws IOException, EnforcerRuleException
+    {
+        File f = temporaryFolder.newFile();
+        FileUtils.fileWrite( f, "line1\nline2\n" );
+
+        rule.setFile( f );
+        rule.setChecksum( "4fcc82a88ee38e0aa16c17f512c685c9" );
+        rule.setType( "md5" );
+        rule.setNormalizeLineSeparatorTo( LineSeparator.UNIX );
+
+        rule.execute( EnforcerTestUtils.getHelper() );
+        // name is not unique therefore compare generated charset
+        Assert.assertEquals( Charset.forName( System.getProperty( "file.encoding" ) ), rule.encoding );
+    }
+
+}
diff --git a/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/utils/TestNormalizeLineSeparatorReader.java b/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/utils/TestNormalizeLineSeparatorReader.java
new file mode 100644
index 0000000..5cfbb1d
--- /dev/null
+++ b/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/utils/TestNormalizeLineSeparatorReader.java
@@ -0,0 +1,77 @@
+package org.apache.maven.plugins.enforcer.utils;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.maven.plugins.enforcer.utils.NormalizeLineSeparatorReader.LineSeparator;
+
+public class TestNormalizeLineSeparatorReader
+    extends TestCase
+{
+    private final static String UNIX_MULTILINE_STRING = "line1\nline2\n\n";
+
+    private final static String WINDOWS_MULTILINE_STRING = "line1\r\nline2\r\n\r\n";
+
+    public void testUnixToWindows()
+        throws IOException
+    {
+        try ( Reader reader =
+            new NormalizeLineSeparatorReader( new StringReader( UNIX_MULTILINE_STRING ), LineSeparator.WINDOWS ) )
+        {
+            assertEquals( WINDOWS_MULTILINE_STRING, IOUtils.toString( reader ) );
+        }
+    }
+
+    public void testUnixToUnix()
+        throws IOException
+    {
+        try ( Reader reader =
+            new NormalizeLineSeparatorReader( new StringReader( UNIX_MULTILINE_STRING ), LineSeparator.UNIX ) )
+        {
+            assertEquals( UNIX_MULTILINE_STRING, IOUtils.toString( reader ) );
+        }
+    }
+
+    public void testWindowsToUnix()
+        throws IOException
+    {
+        try ( Reader reader =
+            new NormalizeLineSeparatorReader( new StringReader( WINDOWS_MULTILINE_STRING ), LineSeparator.UNIX ) )
+        {
+            assertEquals( UNIX_MULTILINE_STRING, IOUtils.toString( reader ) );
+        }
+    }
+
+    public void testWindowsToWindows()
+        throws IOException
+    {
+        try ( Reader reader =
+            new NormalizeLineSeparatorReader( new StringReader( WINDOWS_MULTILINE_STRING ), LineSeparator.WINDOWS ) )
+        {
+            assertEquals( WINDOWS_MULTILINE_STRING, IOUtils.toString( reader ) );
+        }
+    }
+}
diff --git a/pom.xml b/pom.xml
index d5aa796..1d1ea38 100644
--- a/pom.xml
+++ b/pom.xml
@@ -133,7 +133,7 @@
       <dependency>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-lang3</artifactId>
-        <version>3.8.1</version> <!-- commons-lang3 >= 3.8.1 require at least Java 8 -->
+        <version>3.8.1</version> <!-- commons-lang3 > 3.8.1 requires at least Java 8 -->
       </dependency>
       <dependency>
         <groupId>commons-codec</groupId>
@@ -141,6 +141,11 @@
         <version>1.14</version>
       </dependency>
       <dependency>
+        <groupId>commons-io</groupId>
+        <artifactId>commons-io</artifactId>
+        <version>2.5</version><!-- same version as used by maven-shared-utils -->
+      </dependency>
+      <dependency>
         <groupId>org.apache.maven.plugin-testing</groupId>
         <artifactId>maven-plugin-testing-harness</artifactId>
         <version>2.1</version>