diff --git a/CHANGES.md b/CHANGES.md index 8540a44851..c44dfd9050 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,8 @@ This document is intended for Spotless developers. We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Added +* You can now put the filename into a license header template with `$FILE`. ([#1605](https://github.com/diffplug/spotless/pull/1605) fixes [#1147](https://github.com/diffplug/spotless/issues/1147)) ### Changes * We are now opting in to Gradle's new stable configuration cache. ([#1591](https://github.com/diffplug/spotless/pull/1591)) diff --git a/lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java b/lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java index 3b0bf829f8..073af855d3 100644 --- a/lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java +++ b/lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java @@ -143,7 +143,7 @@ public FormatterStep build() { throw new IllegalStateException(yearMode.toString()); } return new Runtime(headerLazy.get(), delimiter, yearSeparator, updateYear, skipLinesMatching); - }, step -> step::format); + }, step -> FormatterFunc.needsFile(step::format)); } if (contentPattern == null) { @@ -214,6 +214,9 @@ private static class Runtime implements Serializable { private final @Nullable String afterYear; private final boolean updateYearWithLatest; private final boolean licenseHeaderWithRange; + private final boolean hasFileToken; + + private static final Pattern FILENAME_PATTERN = Pattern.compile("\\$FILE"); /** The license that we'd like enforced. */ private Runtime(String licenseHeader, String delimiter, String yearSeparator, boolean updateYearWithLatest, @Nullable String skipLinesMatching) { @@ -227,6 +230,7 @@ private Runtime(String licenseHeader, String delimiter, String yearSeparator, bo } this.delimiterPattern = Pattern.compile('^' + delimiter, Pattern.UNIX_LINES | Pattern.MULTILINE); this.skipLinesMatching = skipLinesMatching == null ? null : Pattern.compile(skipLinesMatching); + this.hasFileToken = FILENAME_PATTERN.matcher(licenseHeader).find(); Optional yearToken = getYearToken(licenseHeader); if (yearToken.isPresent()) { @@ -267,9 +271,9 @@ private static Optional getYearToken(String licenseHeader) { } /** Formats the given string. */ - private String format(String raw) { + private String format(String raw, File file) { if (skipLinesMatching == null) { - return addOrUpdateLicenseHeader(raw); + return addOrUpdateLicenseHeader(raw, file); } else { String[] lines = raw.split("\n"); StringBuilder skippedLinesBuilder = new StringBuilder(); @@ -288,11 +292,17 @@ private String format(String raw) { remainingLinesBuilder.append(line).append('\n'); } } - return skippedLinesBuilder + addOrUpdateLicenseHeader(remainingLinesBuilder.toString()); + return skippedLinesBuilder + addOrUpdateLicenseHeader(remainingLinesBuilder.toString(), file); } } - private String addOrUpdateLicenseHeader(String raw) { + private String addOrUpdateLicenseHeader(String raw, File file) { + raw = replaceYear(raw); + raw = replaceFileName(raw, file); + return raw; + } + + private String replaceYear(String raw) { Matcher contentMatcher = delimiterPattern.matcher(raw); if (!contentMatcher.find()) { throw new IllegalArgumentException("Unable to find delimiter regex " + delimiterPattern); @@ -422,6 +432,19 @@ private String setLicenseHeaderYearsFromGitHistory(String raw, File file) throws return beforeYear + yearRange + afterYear + raw.substring(contentMatcher.start()); } + private String replaceFileName(String raw, File file) { + if (!hasFileToken) { + return raw; + } + Matcher contentMatcher = delimiterPattern.matcher(raw); + if (!contentMatcher.find()) { + throw new IllegalArgumentException("Unable to find delimiter regex " + delimiterPattern); + } + String header = raw.substring(0, contentMatcher.start()); + String content = raw.substring(contentMatcher.start()); + return FILENAME_PATTERN.matcher(header).replaceAll(file.getName()) + content; + } + private static String parseYear(String cmd, File file) throws IOException { String fullCmd = cmd + " -- " + file.getAbsolutePath(); ProcessBuilder builder = new ProcessBuilder().directory(file.getParentFile()); diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index c39aff545d..f3e5276d53 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`). ## [Unreleased] +### Added +* You can now put the filename into a license header template with `$FILE`. ([#1605](https://github.com/diffplug/spotless/pull/1605) fixes [#1147](https://github.com/diffplug/spotless/issues/1147)) * `licenseHeader` default pattern for Java files is updated to `(package|import|public|class|module) `. ([#1614](https://github.com/diffplug/spotless/pull/1614)) ## [6.16.0] - 2023-02-27 diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 635459368a..73eb026796 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Added +* You can now put the filename into a license header template with `$FILE`. ([#1605](https://github.com/diffplug/spotless/pull/1605) fixes [#1147](https://github.com/diffplug/spotless/issues/1147)) ### Fixed * `licenseHeader` default pattern for Java files is updated to `(package|import|public|class|module) `. ([#1614](https://github.com/diffplug/spotless/pull/1614)) diff --git a/testlib/src/test/java/com/diffplug/spotless/generic/LicenseHeaderStepTest.java b/testlib/src/test/java/com/diffplug/spotless/generic/LicenseHeaderStepTest.java index 912975dda5..92c6794643 100644 --- a/testlib/src/test/java/com/diffplug/spotless/generic/LicenseHeaderStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/generic/LicenseHeaderStepTest.java @@ -28,6 +28,7 @@ import com.diffplug.spotless.ResourceHarness; import com.diffplug.spotless.SerializableEqualityTester; import com.diffplug.spotless.StepHarness; +import com.diffplug.spotless.StepHarnessWithFile; import com.diffplug.spotless.generic.LicenseHeaderStep.YearMode; class LicenseHeaderStepTest extends ResourceHarness { @@ -35,6 +36,8 @@ class LicenseHeaderStepTest extends ResourceHarness { private static final String package_ = LicenseHeaderStep.DEFAULT_JAVA_HEADER_DELIMITER; private static final String HEADER_WITH_$YEAR = "This is a fake license, $YEAR. ACME corp."; private static final String HEADER_WITH_RANGE_TO_$YEAR = "This is a fake license with range, 2009-$YEAR. ACME corp."; + private static final String HEADER_WITH_$FILE = "This is a fake license, $FILE. ACME corp."; + private static final String HEADER_WITH_$YEAR_$FILE = "This is a fake license, $FILE, $YEAR. ACME corp."; @Test void parseExistingYear() throws Exception { @@ -163,6 +166,16 @@ private String hasHeaderWithRangeAndWithYearTo(String toYear) throws IOException return hasHeaderYear(HEADER_WITH_RANGE_TO_$YEAR, toYear); } + private String hasHeaderFileName(String license, String fileName) throws IOException { + return header(license).replace("$FILE", fileName) + getTestResource(FILE_NO_LICENSE); + } + + private String hasHeaderYearFileName(String license, String year, String fileName) throws IOException { + return header(license) + .replace("$YEAR", year) + .replace("$FILE", fileName) + getTestResource(FILE_NO_LICENSE); + } + private static String currentYear() { return String.valueOf(YearMonth.now().getYear()); } @@ -252,6 +265,36 @@ void should_preserve_year_for_license_with_address() throws Throwable { } @Test + void should_apply_license_containing_filename_token() throws Exception { + FormatterStep step = LicenseHeaderStep.headerDelimiter(header(HEADER_WITH_$FILE), package_).build(); + StepHarnessWithFile.forStep(this, step) + .test(new File("Test.java"), getTestResource(FILE_NO_LICENSE), hasHeaderFileName(HEADER_WITH_$FILE, "Test.java")) + .testUnaffected(new File("Test.java"), hasHeaderFileName(HEADER_WITH_$FILE, "Test.java")); + } + + @Test + void should_update_license_containing_filename_token() throws Exception { + FormatterStep step = LicenseHeaderStep.headerDelimiter(header(HEADER_WITH_$FILE), package_).build(); + StepHarnessWithFile.forStep(this, step) + .test( + new File("After.java"), + hasHeaderFileName(HEADER_WITH_$FILE, "Before.java"), + hasHeaderFileName(HEADER_WITH_$FILE, "After.java")); + } + + @Test + void should_apply_license_containing_YEAR_filename_token() throws Exception { + FormatterStep step = LicenseHeaderStep.headerDelimiter(header(HEADER_WITH_$YEAR_$FILE), package_).build(); + StepHarnessWithFile.forStep(this, step) + .test( + new File("Test.java"), + getTestResource(FILE_NO_LICENSE), + hasHeaderYearFileName(HEADER_WITH_$YEAR_$FILE, currentYear(), "Test.java")) + .testUnaffected( + new File("Test.java"), + hasHeaderYearFileName(HEADER_WITH_$YEAR_$FILE, currentYear(), "Test.java")); + } + void noPackage() throws Throwable { String header = header(getTestResource("license/TestLicense")); FormatterStep step = LicenseHeaderStep.headerDelimiter(header, package_).build();