diff --git a/src/functionalTest/groovy/org/cadixdev/gradle/licenser/LicenserPluginFunctionalTest.groovy b/src/functionalTest/groovy/org/cadixdev/gradle/licenser/LicenserPluginFunctionalTest.groovy index afb9943..952e1aa 100644 --- a/src/functionalTest/groovy/org/cadixdev/gradle/licenser/LicenserPluginFunctionalTest.groovy +++ b/src/functionalTest/groovy/org/cadixdev/gradle/licenser/LicenserPluginFunctionalTest.groovy @@ -444,6 +444,54 @@ class LicenserPluginFunctionalTest extends Specification { where: [gradleVersion, _, extraArgs] << testMatrix + } + @Unroll + def "updates headers in updateLicenses task for files larger than 2048 bytes (gradle #gradleVersion)"() { + given: + def projectDir = temporaryFolder.newFolder() + def sourceDir = projectDir.toPath().resolve(Paths.get("src", "main", "java", "com", "example")).toFile() + sourceDir.mkdirs() + new File(projectDir, "header.txt") << "New copyright header" + new File(projectDir, "settings.gradle") << "" + new File(projectDir, "build.gradle") << """ + plugins { + id('java') + id('org.cadixdev.licenser') + } + + license { + lineEnding = '\\n' + header = project.file('header.txt') + skipExistingHeaders = true + } + """.stripIndent() + def sourceFileContent = """\ + package com.example; + + class MyClass { + } + """.stripIndent() + "// Trailing data\n" * 256 + + def sourceFile = new File(sourceDir, "MyClass.java") << sourceFileContent + + when: + def result = runner(projectDir, gradleVersion, extraArgs + "updateLicenses").build() + + then: + result.task(":updateLicenses").outcome == TaskOutcome.SUCCESS + sourceFile.text.startsWith("""\ + /* + * New copyright header + */ + + package com.example; + + class MyClass { + } + """.stripIndent().trim()) + + where: + [gradleVersion, _, extraArgs] << testMatrix } } diff --git a/src/main/groovy/org/cadixdev/gradle/licenser/header/PreparedCommentHeader.groovy b/src/main/groovy/org/cadixdev/gradle/licenser/header/PreparedCommentHeader.groovy index 2850705..d18e2bb 100644 --- a/src/main/groovy/org/cadixdev/gradle/licenser/header/PreparedCommentHeader.groovy +++ b/src/main/groovy/org/cadixdev/gradle/licenser/header/PreparedCommentHeader.groovy @@ -24,9 +24,11 @@ package org.cadixdev.gradle.licenser.header +import groovy.transform.CompileStatic import groovy.transform.PackageScope import org.cadixdev.gradle.licenser.util.HeaderHelper +@CompileStatic @PackageScope class PreparedCommentHeader implements PreparedHeader { @@ -42,12 +44,12 @@ class PreparedCommentHeader implements PreparedHeader { @Override boolean check(File file, String charset, boolean skipExistingHeaders) throws IOException { - return file.withReader(charset) { BufferedReader reader -> + return new RandomAccessFile(file, "r").with { boolean result = skipExistingHeaders ? - HeaderHelper.contentStartsWithValidHeaderFormat(reader, format) : - HeaderHelper.contentStartsWith(reader, this.lines.iterator(), format.skipLine) + HeaderHelper.contentStartsWithValidHeaderFormat(it, format) : + HeaderHelper.contentStartsWith(it, this.lines.iterator(), format.skipLine) if (result) { - def line = reader.readLine() + def line = it.readLine() if (header.newLine.get()) { result = line != null && line.isEmpty() } else if (line != null) { @@ -73,22 +75,21 @@ class PreparedCommentHeader implements PreparedHeader { // Open file for verifying the license header and reading the text we // need to append after it - file.withReader(charset) { BufferedReader reader -> - if (skipExistingHeaders) { - reader.mark(2048) - def startsWithValidHeader = HeaderHelper.contentStartsWithValidHeaderFormat(reader, format) - if (startsWithValidHeader) { - valid = true - return - } else { - reader.reset() - } + RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); + if (skipExistingHeaders) { + def startsWithValidHeader = HeaderHelper.contentStartsWithValidHeaderFormat(randomAccessFile, format) + if (startsWithValidHeader) { + return false } + } + // Rewind back to the start of the file + randomAccessFile.seek(0L) + randomAccessFile.with { String line while (true) { // Find first non-empty line - line = HeaderHelper.skipEmptyLines(reader) + line = HeaderHelper.skipEmptyLines(it) if (line == null) { return // EOF, invalid and done } @@ -108,7 +109,9 @@ class PreparedCommentHeader implements PreparedHeader { // and the file doesn't have a license header yet if (!(line =~ format.start) || (format.end && line =~ format.end)) { last = line - text = reader.text + byte[] bytes = new byte[it.length() - it.getFilePointer()] + it.readFully(bytes) + text = new String(bytes, charset) return } @@ -133,7 +136,7 @@ class PreparedCommentHeader implements PreparedHeader { } // Read the next line from the file - line = reader.readLine() + line = it.readLine() if (line == null) { // EOF, but the end comment was yet found if (format.end) { @@ -186,7 +189,7 @@ class PreparedCommentHeader implements PreparedHeader { } // Read one more line so we can check for new lines - last = reader.readLine() + last = it.readLine() break } } else if (!(line =~ format.start)) { @@ -224,7 +227,7 @@ class PreparedCommentHeader implements PreparedHeader { if (last != null && HeaderHelper.isBlank(last)) { // Skip empty lines - while ((last = reader.readLine()) != null && HeaderHelper.isBlank(last)) { + while ((last = it.readLine()) != null && HeaderHelper.isBlank(last)) { // Duplicate new lines, NEVER valid valid = false } @@ -232,7 +235,9 @@ class PreparedCommentHeader implements PreparedHeader { if (last != null) { // Read the remaining text from the file so we can add it back later - text = reader.text + byte[] bytes = new byte[it.length() - it.getFilePointer()] + it.readFully(bytes) + text = new String(bytes, charset) } return } @@ -289,5 +294,4 @@ class PreparedCommentHeader implements PreparedHeader { return true } - } diff --git a/src/main/groovy/org/cadixdev/gradle/licenser/util/HeaderHelper.java b/src/main/groovy/org/cadixdev/gradle/licenser/util/HeaderHelper.java index dbe4b01..57150af 100644 --- a/src/main/groovy/org/cadixdev/gradle/licenser/util/HeaderHelper.java +++ b/src/main/groovy/org/cadixdev/gradle/licenser/util/HeaderHelper.java @@ -24,14 +24,17 @@ package org.cadixdev.gradle.licenser.util; +import groovy.transform.CompileStatic; import org.cadixdev.gradle.licenser.header.CommentHeaderFormat; import javax.annotation.Nullable; import java.io.BufferedReader; import java.io.IOException; +import java.io.RandomAccessFile; import java.util.Iterator; import java.util.regex.Pattern; +@CompileStatic public final class HeaderHelper { private HeaderHelper() { @@ -72,9 +75,9 @@ public static String stripTrailingIndent(String s) { return ""; } - public static boolean contentStartsWith(BufferedReader reader, Iterator itr, Pattern ignored) throws IOException { + public static boolean contentStartsWith(RandomAccessFile file, Iterator itr, Pattern ignored) throws IOException { String line; - while (itr.hasNext() && (line = reader.readLine()) != null) { + while (itr.hasNext() && (line = file.readLine()) != null) { if (ignored != null && ignored.matcher(line).find()) { continue; } @@ -87,9 +90,9 @@ public static boolean contentStartsWith(BufferedReader reader, Iterator return !itr.hasNext(); } - public static boolean contentStartsWithValidHeaderFormat(BufferedReader reader, CommentHeaderFormat format) throws IOException { + public static boolean contentStartsWithValidHeaderFormat(RandomAccessFile file, CommentHeaderFormat format) throws IOException { String firstLine; - while ((firstLine = skipEmptyLines(reader)) != null && findPattern(firstLine, format.getSkipLine())) { + while ((firstLine = skipEmptyLines(file)) != null && findPattern(firstLine, format.getSkipLine())) { // skip ignored lines } if (firstLine == null) { @@ -100,7 +103,7 @@ public static boolean contentStartsWithValidHeaderFormat(BufferedReader reader, boolean contentLinesMatch = true; String line; - while ((line = reader.readLine()) != null) { + while ((line = file.readLine()) != null) { // skip ignored lines if (findPattern(line, format.getSkipLine())) { continue; @@ -131,9 +134,10 @@ public static boolean isBlank(String s) { return stripIndent(s).isEmpty(); } - public static String skipEmptyLines(BufferedReader reader) throws IOException { + @Nullable + public static String skipEmptyLines(RandomAccessFile file) throws IOException { String line; - while ((line = reader.readLine()) != null) { + while ((line = file.readLine()) != null) { if (!isBlank(line)) { return line; } @@ -141,5 +145,4 @@ public static String skipEmptyLines(BufferedReader reader) throws IOException { return null; } - } diff --git a/src/test/groovy/org/cadixdev/gradle/licenser/util/HeaderHelperTest.groovy b/src/test/groovy/org/cadixdev/gradle/licenser/util/HeaderHelperTest.groovy index d0c45e1..e09868a 100644 --- a/src/test/groovy/org/cadixdev/gradle/licenser/util/HeaderHelperTest.groovy +++ b/src/test/groovy/org/cadixdev/gradle/licenser/util/HeaderHelperTest.groovy @@ -26,17 +26,20 @@ package org.cadixdev.gradle.licenser.util import org.cadixdev.gradle.licenser.header.HeaderStyle +import org.junit.Rule +import org.junit.rules.TemporaryFolder import spock.lang.Specification class HeaderHelperTest extends Specification { + @Rule + TemporaryFolder temporaryFolder = new TemporaryFolder() + def "contentStartsWithValidHeaderFormat returns false with empty input"() { given: - def inputString = "" - def stringReader = new StringReader(inputString) - def reader = new BufferedReader(stringReader) + def file = new RandomAccessFile(temporaryFolder.newFile(), "r") when: - def result = HeaderHelper.contentStartsWithValidHeaderFormat(reader, HeaderStyle.BLOCK_COMMENT.format) + def result = HeaderHelper.contentStartsWithValidHeaderFormat(file, HeaderStyle.BLOCK_COMMENT.format) then: !result @@ -45,11 +48,11 @@ class HeaderHelperTest extends Specification { def "contentStartsWithValidHeaderFormat returns false with non-matching input"() { given: def inputString = "Not a copyright header" - def stringReader = new StringReader(inputString) - def reader = new BufferedReader(stringReader) + def file = temporaryFolder.newFile() << inputString + def randomAccessFile = new RandomAccessFile(file, "r") when: - def result = HeaderHelper.contentStartsWithValidHeaderFormat(reader, HeaderStyle.BLOCK_COMMENT.format) + def result = HeaderHelper.contentStartsWithValidHeaderFormat(randomAccessFile, HeaderStyle.BLOCK_COMMENT.format) then: !result @@ -63,11 +66,11 @@ class HeaderHelperTest extends Specification { */ My Content """.stripIndent() - def stringReader = new StringReader(inputString) - def reader = new BufferedReader(stringReader) + def file = temporaryFolder.newFile() << inputString + def randomAccessFile = new RandomAccessFile(file, "r") when: - def result = HeaderHelper.contentStartsWithValidHeaderFormat(reader, HeaderStyle.BLOCK_COMMENT.format) + def result = HeaderHelper.contentStartsWithValidHeaderFormat(randomAccessFile, HeaderStyle.BLOCK_COMMENT.format) then: result @@ -81,11 +84,11 @@ class HeaderHelperTest extends Specification { */ My Content """.stripIndent() - def stringReader = new StringReader(inputString) - def reader = new BufferedReader(stringReader) + def file = temporaryFolder.newFile() << inputString + def randomAccessFile = new RandomAccessFile(file, "r") when: - def result = HeaderHelper.contentStartsWithValidHeaderFormat(reader, HeaderStyle.BLOCK_COMMENT.format) + def result = HeaderHelper.contentStartsWithValidHeaderFormat(randomAccessFile, HeaderStyle.BLOCK_COMMENT.format) then: !result @@ -98,11 +101,11 @@ class HeaderHelperTest extends Specification { */ My Content """.stripIndent() - def stringReader = new StringReader(inputString) - def reader = new BufferedReader(stringReader) + def file = temporaryFolder.newFile() << inputString + def randomAccessFile = new RandomAccessFile(file, "r") when: - def result = HeaderHelper.contentStartsWithValidHeaderFormat(reader, HeaderStyle.BLOCK_COMMENT.format) + def result = HeaderHelper.contentStartsWithValidHeaderFormat(randomAccessFile, HeaderStyle.BLOCK_COMMENT.format) then: result @@ -115,11 +118,11 @@ class HeaderHelperTest extends Specification { * Incomplete copyright header My Content """.stripIndent() - def stringReader = new StringReader(inputString) - def reader = new BufferedReader(stringReader) + def file = temporaryFolder.newFile() << inputString + def randomAccessFile = new RandomAccessFile(file, "r") when: - def result = HeaderHelper.contentStartsWithValidHeaderFormat(reader, HeaderStyle.BLOCK_COMMENT.format) + def result = HeaderHelper.contentStartsWithValidHeaderFormat(randomAccessFile, HeaderStyle.BLOCK_COMMENT.format) then: !result @@ -132,11 +135,11 @@ class HeaderHelperTest extends Specification { # Some header My Content """.stripIndent() - def stringReader = new StringReader(inputString) - def reader = new BufferedReader(stringReader) + def file = temporaryFolder.newFile() << inputString + def randomAccessFile = new RandomAccessFile(file, "r") when: - def result = HeaderHelper.contentStartsWithValidHeaderFormat(reader, HeaderStyle.HASH.format) + def result = HeaderHelper.contentStartsWithValidHeaderFormat(randomAccessFile, HeaderStyle.HASH.format) then: result @@ -152,11 +155,11 @@ class HeaderHelperTest extends Specification { """.stripIndent() - def stringReader = new StringReader(inputString) - def reader = new BufferedReader(stringReader) + def file = temporaryFolder.newFile() << inputString + def randomAccessFile = new RandomAccessFile(file, "r") when: - def result = HeaderHelper.contentStartsWithValidHeaderFormat(reader, HeaderStyle.XML.format) + def result = HeaderHelper.contentStartsWithValidHeaderFormat(randomAccessFile, HeaderStyle.XML.format) then: result