From 02836db08d0e8ac4c7f2eb03a41a49b7f54b7f07 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Tue, 2 Jun 2020 14:56:51 -0700 Subject: [PATCH 01/10] Make the license header more robust in parsing existing dates, and add the ability to update existing dates. --- .../spotless/generic/LicenseHeaderStep.java | 148 ++++++++++++------ .../gradle/spotless/FormatExtension.java | 11 ++ .../generic/LicenseHeaderStepTest.java | 6 +- 3 files changed, 111 insertions(+), 54 deletions(-) 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 384738af84..93b4546a23 100644 --- a/lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java +++ b/lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java @@ -28,6 +28,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.Nullable; + import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.LineEnding; import com.diffplug.spotless.SerializableFileFilter; @@ -38,52 +40,50 @@ public final class LicenseHeaderStep implements Serializable { private static final String NAME = "licenseHeader"; private static final String DEFAULT_YEAR_DELIMITER = "-"; + private static final boolean DEFAULT_OVERWRITE_YEAR_WITH_LATEST = false; private static final List YEAR_TOKENS = Arrays.asList("$YEAR", "$today.year"); private static final SerializableFileFilter UNSUPPORTED_JVM_FILES_FILTER = SerializableFileFilter.skipFilesNamed( "package-info.java", "package-info.groovy", "module-info.java"); - private final String licenseHeader; - private final boolean hasYearToken; - private final Pattern delimiterPattern; - private final Pattern yearMatcherPattern; - private final String licenseHeaderBeforeYearToken; - private final String licenseHeaderAfterYearToken; - private final String licenseHeaderWithYearTokenReplaced; - - /** Creates a FormatterStep which forces the start of each file to match a license header. */ + /** @see #createFromHeader(String, String, String, boolean) */ public static FormatterStep createFromHeader(String licenseHeader, String delimiter) { return createFromHeader(licenseHeader, delimiter, DEFAULT_YEAR_DELIMITER); } + /** @see #createFromHeader(String, String, String, boolean) */ public static FormatterStep createFromHeader(String licenseHeader, String delimiter, String yearSeparator) { + return createFromHeader(licenseHeader, delimiter, yearSeparator, DEFAULT_OVERWRITE_YEAR_WITH_LATEST); + } + + /** Creates a FormatterStep which forces the start of each file to match a license header. */ + public static FormatterStep createFromHeader(String licenseHeader, String delimiter, String yearSeparator, boolean overwriteYearWithLatest) { Objects.requireNonNull(licenseHeader, "licenseHeader"); Objects.requireNonNull(delimiter, "delimiter"); Objects.requireNonNull(yearSeparator, "yearSeparator"); return FormatterStep.create(LicenseHeaderStep.NAME, - new LicenseHeaderStep(licenseHeader, delimiter, yearSeparator), + new LicenseHeaderStep(licenseHeader, delimiter, yearSeparator, overwriteYearWithLatest), step -> step::format); } - /** - * Creates a FormatterStep which forces the start of each file to match the license header - * contained in the given file. - */ + /** @see #createFromFile(File, Charset, String, String, boolean) */ public static FormatterStep createFromFile(File licenseHeaderFile, Charset encoding, String delimiter) { return createFromFile(licenseHeaderFile, encoding, delimiter, DEFAULT_YEAR_DELIMITER); } - /** - * Creates a FormatterStep which forces the start of each file to match the license header - * contained in the given file. - */ + /** @see #createFromFile(File, Charset, String, String, boolean) */ public static FormatterStep createFromFile(File licenseHeaderFile, Charset encoding, String delimiter, String yearSeparator) { + return createFromFile(licenseHeaderFile, encoding, delimiter, yearSeparator, DEFAULT_OVERWRITE_YEAR_WITH_LATEST); + } + + /** Creates a FormatterStep which forces the start of each file to match a license header. */ + public static FormatterStep createFromFile(File licenseHeaderFile, Charset encoding, String delimiter, String yearSeparator, boolean overwriteYearWithLatest) { Objects.requireNonNull(licenseHeaderFile, "licenseHeaderFile"); Objects.requireNonNull(encoding, "encoding"); Objects.requireNonNull(delimiter, "delimiter"); Objects.requireNonNull(yearSeparator, "yearSeparator"); return FormatterStep.createLazy(LicenseHeaderStep.NAME, - () -> new LicenseHeaderStep(licenseHeaderFile, encoding, delimiter, yearSeparator), + () -> new LicenseHeaderStep(licenseHeaderFile, encoding, delimiter, yearSeparator, overwriteYearWithLatest), step -> step::format); } @@ -95,12 +95,23 @@ public static String defaultYearDelimiter() { return DEFAULT_YEAR_DELIMITER; } + public static boolean defaultOverwriteYearWithLatest() { + return DEFAULT_OVERWRITE_YEAR_WITH_LATEST; + } + public static SerializableFileFilter unsupportedJvmFilesFilter() { return UNSUPPORTED_JVM_FILES_FILTER; } + private final Pattern delimiterPattern; + private final String yearSepOrFull; + private final @Nullable String yearToday; + private final @Nullable String beforeYear; + private final @Nullable String afterYear; + private final boolean overwriteYearWithLatest; + /** The license that we'd like enforced. */ - private LicenseHeaderStep(String licenseHeader, String delimiter, String yearSeparator) { + private LicenseHeaderStep(String licenseHeader, String delimiter, String yearSeparator, boolean overwriteYearWithLatest) { if (delimiter.contains("\n")) { throw new IllegalArgumentException("The delimiter must not contain any newlines."); } @@ -109,25 +120,27 @@ private LicenseHeaderStep(String licenseHeader, String delimiter, String yearSep if (!licenseHeader.endsWith("\n")) { licenseHeader = licenseHeader + "\n"; } - this.licenseHeader = licenseHeader; this.delimiterPattern = Pattern.compile('^' + delimiter, Pattern.UNIX_LINES | Pattern.MULTILINE); Optional yearToken = getYearToken(licenseHeader); - this.hasYearToken = yearToken.isPresent(); - if (hasYearToken) { + if (yearToken.isPresent()) { + yearToday = String.valueOf(YearMonth.now().getYear()); int yearTokenIndex = licenseHeader.indexOf(yearToken.get()); - licenseHeaderBeforeYearToken = licenseHeader.substring(0, yearTokenIndex); - licenseHeaderAfterYearToken = licenseHeader.substring(yearTokenIndex + 5); - licenseHeaderWithYearTokenReplaced = licenseHeader.replace(yearToken.get(), String.valueOf(YearMonth.now().getYear())); - yearMatcherPattern = Pattern.compile("[0-9]{4}(" + Pattern.quote(yearSeparator) + "[0-9]{4})?"); + beforeYear = licenseHeader.substring(0, yearTokenIndex); + afterYear = licenseHeader.substring(yearTokenIndex + yearToken.get().length()); + yearSepOrFull = yearSeparator; + this.overwriteYearWithLatest = overwriteYearWithLatest; } else { - licenseHeaderBeforeYearToken = null; - licenseHeaderAfterYearToken = null; - licenseHeaderWithYearTokenReplaced = null; - yearMatcherPattern = null; + yearToday = null; + beforeYear = null; + afterYear = null; + this.yearSepOrFull = licenseHeader; + this.overwriteYearWithLatest = false; } } + private static final Pattern patternYearSingle = Pattern.compile("[0-9]{4}"); + /** * Get the first place holder token being used in the * license header for specifying the year @@ -140,38 +153,71 @@ private static Optional getYearToken(String licenseHeader) { } /** Reads the license file from the given file. */ - private LicenseHeaderStep(File licenseFile, Charset encoding, String delimiter, String yearSeparator) throws IOException { - this(new String(Files.readAllBytes(licenseFile.toPath()), encoding), delimiter, yearSeparator); + private LicenseHeaderStep(File licenseFile, Charset encoding, String delimiter, String yearSeparator, boolean overwriteYearWithLatest) throws IOException { + this(new String(Files.readAllBytes(licenseFile.toPath()), encoding), delimiter, yearSeparator, overwriteYearWithLatest); } /** Formats the given string. */ public String format(String raw) { - Matcher matcher = delimiterPattern.matcher(raw); - if (!matcher.find()) { + Matcher contentMatcher = delimiterPattern.matcher(raw); + if (!contentMatcher.find()) { throw new IllegalArgumentException("Unable to find delimiter regex " + delimiterPattern); } else { - if (hasYearToken) { - if (matchesLicenseWithYearToken(raw, matcher)) { - // that means we have the license like `licenseHeaderBeforeYearToken 1990-2015 licenseHeaderAfterYearToken` + if (yearToday == null) { + // the no year case is easy + if (contentMatcher.start() == yearSepOrFull.length() && raw.startsWith(yearSepOrFull)) { + // if no change is required, return the raw string without + // creating any other new strings for maximum performance return raw; } else { - return licenseHeaderWithYearTokenReplaced + raw.substring(matcher.start()); + // otherwise we'll have to add the header + return yearSepOrFull + raw.substring(contentMatcher.start()); } - } else if (matcher.start() == licenseHeader.length() && raw.startsWith(licenseHeader)) { - // if no change is required, return the raw string without - // creating any other new strings for maximum performance - return raw; } else { - // otherwise we'll have to add the header - return licenseHeader + raw.substring(matcher.start()); + // the yes year case is a bit harder + int beforeYearIdx = raw.indexOf(beforeYear); + int afterYearIdx = raw.indexOf(afterYear, beforeYearIdx + beforeYear.length() + 1); + + if (beforeYearIdx >= 0 && afterYearIdx >= 0 && afterYearIdx + afterYear.length() <= contentMatcher.start()) { + boolean noPadding = beforeYearIdx == 0 && afterYearIdx + afterYear.length() == contentMatcher.start(); // allows fastpath return raw + String parsedYear = raw.substring(beforeYearIdx + beforeYear.length(), afterYearIdx); + if (parsedYear.equals(yearToday)) { + // it's good as is! + return noPadding ? raw : beforeYear + yearToday + afterYear + raw.substring(contentMatcher.start()); + } else if (patternYearSingle.matcher(parsedYear).matches()) { + if (overwriteYearWithLatest) { + // expand from `2004` to `2004-2020` + return beforeYear + parsedYear + yearSepOrFull + yearToday + afterYear + raw.substring(contentMatcher.start()); + } else { + // it's already good as a single year + return noPadding ? raw : beforeYear + parsedYear + afterYear + raw.substring(contentMatcher.start()); + } + } else { + Matcher yearMatcher = patternYearSingle.matcher(parsedYear); + if (yearMatcher.find()) { + String firstYear = yearMatcher.group(); + String newYear; + String secondYear; + if (overwriteYearWithLatest) { + secondYear = firstYear.equals(yearToday) ? null : yearToday; + } else if (yearMatcher.find(yearMatcher.end() + 1)) { + secondYear = yearMatcher.group(); + } else { + secondYear = null; + } + if (secondYear == null) { + newYear = firstYear; + } else { + newYear = firstYear + yearSepOrFull + secondYear; + } + return noPadding && newYear.equals(parsedYear) ? raw : beforeYear + newYear + afterYear + raw.substring(contentMatcher.start()); + } + } + } + // at worst, we just say that it was made today + return beforeYear + yearToday + afterYear + raw.substring(contentMatcher.start()); } } } - private boolean matchesLicenseWithYearToken(String raw, Matcher matcher) { - int startOfTheSecondPart = raw.indexOf(licenseHeaderAfterYearToken); - return startOfTheSecondPart > licenseHeaderBeforeYearToken.length() - && (raw.startsWith(licenseHeaderBeforeYearToken) && startOfTheSecondPart + licenseHeaderAfterYearToken.length() == matcher.start()) - && yearMatcherPattern.matcher(raw.substring(licenseHeaderBeforeYearToken.length(), startOfTheSecondPart)).matches(); - } } diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java index 7e65fa5371..308a9fcc17 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java @@ -409,6 +409,7 @@ public void indentWithTabs() { public abstract class LicenseHeaderConfig { String delimiter; String yearSeparator = LicenseHeaderStep.defaultYearDelimiter(); + boolean overwriteYearLatest = LicenseHeaderStep.defaultOverwriteYearWithLatest(); public LicenseHeaderConfig(String delimiter) { this.delimiter = Objects.requireNonNull(delimiter, "delimiter"); @@ -434,6 +435,16 @@ public LicenseHeaderConfig yearSeparator(String yearSeparator) { return this; } + /** + * @param overwriteYearLatest + * Will turn `2004` into `2004-2020`, and `2004-2019` into `2004-2020` + */ + public LicenseHeaderConfig overwriteYearLatest(boolean overwriteYearLatest) { + this.overwriteYearLatest = overwriteYearLatest; + replaceStep(createStep()); + return this; + } + abstract FormatterStep createStep(); } 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 96b05ba5f9..1688e8cc30 100644 --- a/testlib/src/test/java/com/diffplug/spotless/generic/LicenseHeaderStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/generic/LicenseHeaderStepTest.java @@ -71,7 +71,7 @@ public void should_apply_license_containing_YEAR_token() throws Throwable { .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "2003")) .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990-2015")) .test(fileWithLicenseContaining("Something before license.*/\n/* \n * " + LICENSE_HEADER_YEAR, "2003"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())) - .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR + "\n **/\n/* Something after license.", "2003"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())) + .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR + "\n **/\n/* Something after license.", "2003"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, "2003")) .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "not a year"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())); // Check with variant @@ -98,7 +98,7 @@ public void should_apply_license_containing_YEAR_token_with_non_default_year_sep StepHarness.forStep(step) .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990, 2015")) - .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990-2015"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())); + .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990-2015"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990, 2015")); } @Test @@ -107,7 +107,7 @@ public void should_apply_license_containing_YEAR_token_with_special_character_in StepHarness.forStep(step) .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990(2015")) - .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990-2015"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())); + .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990-2015"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990(2015")); } @Test From e62102e372f12f9504cd1aa15710281f8ad2af51 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Tue, 2 Jun 2020 15:01:28 -0700 Subject: [PATCH 02/10] Add test which exercises the `overwriteYearWithLatest` functionality. --- .../diffplug/spotless/generic/LicenseHeaderStepTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) 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 1688e8cc30..9eb06cc02b 100644 --- a/testlib/src/test/java/com/diffplug/spotless/generic/LicenseHeaderStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/generic/LicenseHeaderStepTest.java @@ -92,6 +92,15 @@ public void should_apply_license_containing_YEAR_token() throws Throwable { .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR_INTELLIJ_TOKEN), fileWithLicenseContaining(LICENSE_HEADER_YEAR_INTELLIJ_TOKEN, currentYear(), "$today.year")); } + @Test + public void overwriteYearWithLatest() throws Throwable { + FormatterStep step = LicenseHeaderStep.createFromFile(createLicenseWith(LICENSE_HEADER_YEAR), StandardCharsets.UTF_8, LICENSE_HEADER_DELIMITER, "-", true); + StepHarness.forStep(step) + .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())) + .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "2003"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, "2003-" + currentYear())) + .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990-2015"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990-" + currentYear())); + } + @Test public void should_apply_license_containing_YEAR_token_with_non_default_year_separator() throws Throwable { FormatterStep step = LicenseHeaderStep.createFromFile(createLicenseWith(LICENSE_HEADER_YEAR), StandardCharsets.UTF_8, LICENSE_HEADER_DELIMITER, ", "); From d04d2b53392835c8057c0daf0c33948931ec523b Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Tue, 2 Jun 2020 16:01:49 -0700 Subject: [PATCH 03/10] Rework LicenseHeaderStep to allow plugin-gradle to set default value lazily based on `ratchetFrom`. --- .../spotless/generic/LicenseHeaderStep.java | 24 ++--- .../gradle/spotless/FormatExtension.java | 36 ++++--- .../gradle/spotless/LicenseHeaderTest.java | 95 +++++++++++++++++++ .../generic/LicenseHeaderStepTest.java | 2 +- 4 files changed, 131 insertions(+), 26 deletions(-) create mode 100644 plugin-gradle/src/test/java/com/diffplug/gradle/spotless/LicenseHeaderTest.java 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 93b4546a23..b5fbb5cd7f 100644 --- a/lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java +++ b/lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java @@ -57,12 +57,12 @@ public static FormatterStep createFromHeader(String licenseHeader, String delimi } /** Creates a FormatterStep which forces the start of each file to match a license header. */ - public static FormatterStep createFromHeader(String licenseHeader, String delimiter, String yearSeparator, boolean overwriteYearWithLatest) { + public static FormatterStep createFromHeader(String licenseHeader, String delimiter, String yearSeparator, boolean updateYearWithLatest) { Objects.requireNonNull(licenseHeader, "licenseHeader"); Objects.requireNonNull(delimiter, "delimiter"); Objects.requireNonNull(yearSeparator, "yearSeparator"); return FormatterStep.create(LicenseHeaderStep.NAME, - new LicenseHeaderStep(licenseHeader, delimiter, yearSeparator, overwriteYearWithLatest), + new LicenseHeaderStep(licenseHeader, delimiter, yearSeparator, updateYearWithLatest), step -> step::format); } @@ -77,13 +77,13 @@ public static FormatterStep createFromFile(File licenseHeaderFile, Charset encod } /** Creates a FormatterStep which forces the start of each file to match a license header. */ - public static FormatterStep createFromFile(File licenseHeaderFile, Charset encoding, String delimiter, String yearSeparator, boolean overwriteYearWithLatest) { + public static FormatterStep createFromFile(File licenseHeaderFile, Charset encoding, String delimiter, String yearSeparator, boolean updateYearWithLatest) { Objects.requireNonNull(licenseHeaderFile, "licenseHeaderFile"); Objects.requireNonNull(encoding, "encoding"); Objects.requireNonNull(delimiter, "delimiter"); Objects.requireNonNull(yearSeparator, "yearSeparator"); return FormatterStep.createLazy(LicenseHeaderStep.NAME, - () -> new LicenseHeaderStep(licenseHeaderFile, encoding, delimiter, yearSeparator, overwriteYearWithLatest), + () -> new LicenseHeaderStep(licenseHeaderFile, encoding, delimiter, yearSeparator, updateYearWithLatest), step -> step::format); } @@ -108,10 +108,10 @@ public static SerializableFileFilter unsupportedJvmFilesFilter() { private final @Nullable String yearToday; private final @Nullable String beforeYear; private final @Nullable String afterYear; - private final boolean overwriteYearWithLatest; + private final boolean updateYearWithLatest; /** The license that we'd like enforced. */ - private LicenseHeaderStep(String licenseHeader, String delimiter, String yearSeparator, boolean overwriteYearWithLatest) { + public LicenseHeaderStep(String licenseHeader, String delimiter, String yearSeparator, boolean updateYearWithLatest) { if (delimiter.contains("\n")) { throw new IllegalArgumentException("The delimiter must not contain any newlines."); } @@ -129,13 +129,13 @@ private LicenseHeaderStep(String licenseHeader, String delimiter, String yearSep beforeYear = licenseHeader.substring(0, yearTokenIndex); afterYear = licenseHeader.substring(yearTokenIndex + yearToken.get().length()); yearSepOrFull = yearSeparator; - this.overwriteYearWithLatest = overwriteYearWithLatest; + this.updateYearWithLatest = updateYearWithLatest; } else { yearToday = null; beforeYear = null; afterYear = null; this.yearSepOrFull = licenseHeader; - this.overwriteYearWithLatest = false; + this.updateYearWithLatest = false; } } @@ -153,8 +153,8 @@ private static Optional getYearToken(String licenseHeader) { } /** Reads the license file from the given file. */ - private LicenseHeaderStep(File licenseFile, Charset encoding, String delimiter, String yearSeparator, boolean overwriteYearWithLatest) throws IOException { - this(new String(Files.readAllBytes(licenseFile.toPath()), encoding), delimiter, yearSeparator, overwriteYearWithLatest); + public LicenseHeaderStep(File licenseFile, Charset encoding, String delimiter, String yearSeparator, boolean updateYearWithLatest) throws IOException { + this(new String(Files.readAllBytes(licenseFile.toPath()), encoding), delimiter, yearSeparator, updateYearWithLatest); } /** Formats the given string. */ @@ -185,7 +185,7 @@ public String format(String raw) { // it's good as is! return noPadding ? raw : beforeYear + yearToday + afterYear + raw.substring(contentMatcher.start()); } else if (patternYearSingle.matcher(parsedYear).matches()) { - if (overwriteYearWithLatest) { + if (updateYearWithLatest) { // expand from `2004` to `2004-2020` return beforeYear + parsedYear + yearSepOrFull + yearToday + afterYear + raw.substring(contentMatcher.start()); } else { @@ -198,7 +198,7 @@ public String format(String raw) { String firstYear = yearMatcher.group(); String newYear; String secondYear; - if (overwriteYearWithLatest) { + if (updateYearWithLatest) { secondYear = firstYear.equals(yearToday) ? null : yearToday; } else if (yearMatcher.find(yearMatcher.end() + 1)) { secondYear = yearMatcher.group(); diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java index 308a9fcc17..01608acc01 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java @@ -18,8 +18,10 @@ import static com.diffplug.gradle.spotless.PluginGradlePreconditions.requireElementsNonNull; import java.io.File; +import java.io.IOException; import java.io.Serializable; import java.nio.charset.Charset; +import java.nio.file.Files; import java.util.*; import java.util.stream.Stream; @@ -409,7 +411,7 @@ public void indentWithTabs() { public abstract class LicenseHeaderConfig { String delimiter; String yearSeparator = LicenseHeaderStep.defaultYearDelimiter(); - boolean overwriteYearLatest = LicenseHeaderStep.defaultOverwriteYearWithLatest(); + Boolean updateYearWithLatest = null; public LicenseHeaderConfig(String delimiter) { this.delimiter = Objects.requireNonNull(delimiter, "delimiter"); @@ -436,20 +438,28 @@ public LicenseHeaderConfig yearSeparator(String yearSeparator) { } /** - * @param overwriteYearLatest + * @param updateYearWithLatest * Will turn `2004` into `2004-2020`, and `2004-2019` into `2004-2020` + * Default value is false, unless {@link SpotlessExtension#ratchetFrom(String)} is used, in which case default value is true. */ - public LicenseHeaderConfig overwriteYearLatest(boolean overwriteYearLatest) { - this.overwriteYearLatest = overwriteYearLatest; + public LicenseHeaderConfig updateYearWithLatest(boolean overwriteYearLatest) { + this.updateYearWithLatest = overwriteYearLatest; replaceStep(createStep()); return this; } - abstract FormatterStep createStep(); + protected abstract String licenseHeader() throws IOException; + + FormatterStep createStep() { + return FormatterStep.createLazy(LicenseHeaderStep.name(), () -> { + // by default, we should update the year if the user is using ratchetFrom + boolean updateYear = updateYearWithLatest == null ? FormatExtension.this.root.getRatchetFrom() != null : updateYearWithLatest; + return new LicenseHeaderStep(licenseHeader(), delimiter, yearSeparator, updateYear); + }, step -> step::format); + } } public class LicenseStringHeaderConfig extends LicenseHeaderConfig { - private String header; LicenseStringHeaderConfig(String delimiter, String header) { @@ -457,13 +467,13 @@ public class LicenseStringHeaderConfig extends LicenseHeaderConfig { this.header = Objects.requireNonNull(header, "header"); } - FormatterStep createStep() { - return LicenseHeaderStep.createFromHeader(header, delimiter, yearSeparator); + @Override + protected String licenseHeader() { + return header; } } public class LicenseFileHeaderConfig extends LicenseHeaderConfig { - private Object headerFile; LicenseFileHeaderConfig(String delimiter, Object headerFile) { @@ -471,10 +481,10 @@ public class LicenseFileHeaderConfig extends LicenseHeaderConfig { this.headerFile = Objects.requireNonNull(headerFile, "headerFile"); } - FormatterStep createStep() { - return LicenseHeaderStep - .createFromFile(getProject().file(headerFile), getEncoding(), delimiter, - yearSeparator); + @Override + protected String licenseHeader() throws IOException { + byte[] content = Files.readAllBytes(getProject().file(headerFile).toPath()); + return new String(content, getEncoding()); } } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/LicenseHeaderTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/LicenseHeaderTest.java new file mode 100644 index 0000000000..7a66f45c6d --- /dev/null +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/LicenseHeaderTest.java @@ -0,0 +1,95 @@ +/* + * Copyright 2020 DiffPlug + * + * 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.diffplug.gradle.spotless; + +import java.io.IOException; +import java.time.YearMonth; + +import org.eclipse.jgit.api.Git; +import org.junit.Test; + +public class LicenseHeaderTest extends GradleIntegrationTest { + private static final String NOW = String.valueOf(YearMonth.now().getYear()); + + private static final String TEST_JAVA = "src/main/java/pkg/Test.java"; + private static final String CONTENT = "package pkg;\npublic class Test {}"; + + private void setLicenseStep(String licenseLine) throws IOException { + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.gradle.spotless'", + " id 'java'", + "}", + "spotless {", + " java {", + licenseLine, + " }", + "}"); + } + + private void assertUnchanged(String year) throws IOException { + assertTransform(year, year); + } + + private void assertTransform(String yearBefore, String yearAfter) throws IOException { + setFile(TEST_JAVA).toContent("/** " + yearBefore + " */\n" + CONTENT); + gradleRunner().withArguments("spotlessApply", "--stacktrace").build(); + assertFile(TEST_JAVA).hasContent("/** " + yearAfter + " */\n" + CONTENT); + } + + private void testSuiteUpdateWithLatest(boolean update) throws IOException { + if (update) { + assertTransform("2003", "2003-" + NOW); + assertTransform("2003-2005", "2003-" + NOW); + } else { + assertUnchanged("2003"); + assertUnchanged("2003-2005"); + } + assertUnchanged(NOW); + assertTransform("", NOW); + } + + @Test + public void normal() throws IOException { + setLicenseStep("licenseHeader('/** $YEAR */')"); + testSuiteUpdateWithLatest(false); + } + + @Test + public void updateYearWithLatestTrue() throws IOException { + setLicenseStep("licenseHeader('/** $YEAR */').updateYearWithLatest(true)"); + testSuiteUpdateWithLatest(true); + } + + @Test + public void ratchetFrom() throws Exception { + try (Git git = Git.init().setDirectory(rootFolder()).call()) { + git.commit().setMessage("First commit").call(); + } + setLicenseStep("licenseHeader('/** $YEAR */')\nratchetFrom 'HEAD'"); + testSuiteUpdateWithLatest(true); + } + + @Test + public void ratchetFromButUpdateFalse() throws Exception { + try (Git git = Git.init().setDirectory(rootFolder()).call()) { + git.commit().setMessage("First commit").call(); + } + Git.init().setDirectory(rootFolder()).call(); + setLicenseStep("licenseHeader('/** $YEAR */').updateYearWithLatest(false)\nratchetFrom 'HEAD'"); + testSuiteUpdateWithLatest(false); + } +} 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 9eb06cc02b..554b0473a4 100644 --- a/testlib/src/test/java/com/diffplug/spotless/generic/LicenseHeaderStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/generic/LicenseHeaderStepTest.java @@ -93,7 +93,7 @@ public void should_apply_license_containing_YEAR_token() throws Throwable { } @Test - public void overwriteYearWithLatest() throws Throwable { + public void updateYearWithLatest() throws Throwable { FormatterStep step = LicenseHeaderStep.createFromFile(createLicenseWith(LICENSE_HEADER_YEAR), StandardCharsets.UTF_8, LICENSE_HEADER_DELIMITER, "-", true); StepHarness.forStep(step) .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())) From c9bf65b3e12a090843b05a3e3c37f575731523f2 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Tue, 2 Jun 2020 16:30:47 -0700 Subject: [PATCH 04/10] Fix license header. --- .../java/com/diffplug/gradle/spotless/LicenseHeaderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/LicenseHeaderTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/LicenseHeaderTest.java index 7a66f45c6d..efb810f761 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/LicenseHeaderTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/LicenseHeaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 DiffPlug + * Copyright 2016 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 09f9f6d3e645145fbec640aff4e49709df051aa8 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Tue, 2 Jun 2020 16:39:09 -0700 Subject: [PATCH 05/10] Reduce the diff. --- .../spotless/generic/LicenseHeaderStep.java | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) 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 b5fbb5cd7f..c538925ea3 100644 --- a/lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java +++ b/lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java @@ -40,50 +40,44 @@ public final class LicenseHeaderStep implements Serializable { private static final String NAME = "licenseHeader"; private static final String DEFAULT_YEAR_DELIMITER = "-"; - private static final boolean DEFAULT_OVERWRITE_YEAR_WITH_LATEST = false; private static final List YEAR_TOKENS = Arrays.asList("$YEAR", "$today.year"); private static final SerializableFileFilter UNSUPPORTED_JVM_FILES_FILTER = SerializableFileFilter.skipFilesNamed( "package-info.java", "package-info.groovy", "module-info.java"); - /** @see #createFromHeader(String, String, String, boolean) */ + /** Creates a FormatterStep which forces the start of each file to match a license header. */ public static FormatterStep createFromHeader(String licenseHeader, String delimiter) { return createFromHeader(licenseHeader, delimiter, DEFAULT_YEAR_DELIMITER); } - /** @see #createFromHeader(String, String, String, boolean) */ public static FormatterStep createFromHeader(String licenseHeader, String delimiter, String yearSeparator) { - return createFromHeader(licenseHeader, delimiter, yearSeparator, DEFAULT_OVERWRITE_YEAR_WITH_LATEST); - } - - /** Creates a FormatterStep which forces the start of each file to match a license header. */ - public static FormatterStep createFromHeader(String licenseHeader, String delimiter, String yearSeparator, boolean updateYearWithLatest) { Objects.requireNonNull(licenseHeader, "licenseHeader"); Objects.requireNonNull(delimiter, "delimiter"); Objects.requireNonNull(yearSeparator, "yearSeparator"); return FormatterStep.create(LicenseHeaderStep.NAME, - new LicenseHeaderStep(licenseHeader, delimiter, yearSeparator, updateYearWithLatest), + new LicenseHeaderStep(licenseHeader, delimiter, yearSeparator), step -> step::format); } - /** @see #createFromFile(File, Charset, String, String, boolean) */ + /** + * Creates a FormatterStep which forces the start of each file to match the license header + * contained in the given file. + */ public static FormatterStep createFromFile(File licenseHeaderFile, Charset encoding, String delimiter) { return createFromFile(licenseHeaderFile, encoding, delimiter, DEFAULT_YEAR_DELIMITER); } - /** @see #createFromFile(File, Charset, String, String, boolean) */ + /** + * Creates a FormatterStep which forces the start of each file to match the license header + * contained in the given file. + */ public static FormatterStep createFromFile(File licenseHeaderFile, Charset encoding, String delimiter, String yearSeparator) { - return createFromFile(licenseHeaderFile, encoding, delimiter, yearSeparator, DEFAULT_OVERWRITE_YEAR_WITH_LATEST); - } - - /** Creates a FormatterStep which forces the start of each file to match a license header. */ - public static FormatterStep createFromFile(File licenseHeaderFile, Charset encoding, String delimiter, String yearSeparator, boolean updateYearWithLatest) { Objects.requireNonNull(licenseHeaderFile, "licenseHeaderFile"); Objects.requireNonNull(encoding, "encoding"); Objects.requireNonNull(delimiter, "delimiter"); Objects.requireNonNull(yearSeparator, "yearSeparator"); return FormatterStep.createLazy(LicenseHeaderStep.NAME, - () -> new LicenseHeaderStep(licenseHeaderFile, encoding, delimiter, yearSeparator, updateYearWithLatest), + () -> new LicenseHeaderStep(new String(Files.readAllBytes(licenseHeaderFile.toPath()), encoding), delimiter, yearSeparator), step -> step::format); } @@ -95,10 +89,6 @@ public static String defaultYearDelimiter() { return DEFAULT_YEAR_DELIMITER; } - public static boolean defaultOverwriteYearWithLatest() { - return DEFAULT_OVERWRITE_YEAR_WITH_LATEST; - } - public static SerializableFileFilter unsupportedJvmFilesFilter() { return UNSUPPORTED_JVM_FILES_FILTER; } @@ -110,6 +100,10 @@ public static SerializableFileFilter unsupportedJvmFilesFilter() { private final @Nullable String afterYear; private final boolean updateYearWithLatest; + private LicenseHeaderStep(String licenseHeader, String delimiter, String yearSeparator) { + this(licenseHeader, delimiter, yearSeparator, false); + } + /** The license that we'd like enforced. */ public LicenseHeaderStep(String licenseHeader, String delimiter, String yearSeparator, boolean updateYearWithLatest) { if (delimiter.contains("\n")) { From 15d8fedec88bbed940c98e8c43423c4490227f62 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Tue, 2 Jun 2020 16:40:33 -0700 Subject: [PATCH 06/10] Reduce the diff even further. --- .../com/diffplug/spotless/generic/LicenseHeaderStep.java | 7 ------- 1 file changed, 7 deletions(-) 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 c538925ea3..0a9b0cabb6 100644 --- a/lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java +++ b/lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java @@ -16,7 +16,6 @@ package com.diffplug.spotless.generic; import java.io.File; -import java.io.IOException; import java.io.Serializable; import java.nio.charset.Charset; import java.nio.file.Files; @@ -146,11 +145,6 @@ private static Optional getYearToken(String licenseHeader) { return YEAR_TOKENS.stream().filter(licenseHeader::contains).findFirst(); } - /** Reads the license file from the given file. */ - public LicenseHeaderStep(File licenseFile, Charset encoding, String delimiter, String yearSeparator, boolean updateYearWithLatest) throws IOException { - this(new String(Files.readAllBytes(licenseFile.toPath()), encoding), delimiter, yearSeparator, updateYearWithLatest); - } - /** Formats the given string. */ public String format(String raw) { Matcher contentMatcher = delimiterPattern.matcher(raw); @@ -213,5 +207,4 @@ public String format(String raw) { } } } - } From ab5508fdfc27c85c020f35b33340dcd82824c034 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Tue, 2 Jun 2020 17:38:54 -0700 Subject: [PATCH 07/10] Update documentation and javadoc. --- CHANGES.md | 4 +++ plugin-gradle/CHANGES.md | 6 ++++ plugin-gradle/README.md | 29 ++++--------------- .../gradle/spotless/FormatExtension.java | 9 ++++-- plugin-maven/CHANGES.md | 2 ++ 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7344279dcb..a6f494e7e0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,10 @@ 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 +* `LicenseHeaderStep` now has an `updateYearWithLatest` parameter which can update copyright headers to today's date. ([#593](https://github.com/diffplug/spotless/pull/593)) + * Parsing of existing years from headers is now more lenient. + * The `LicenseHeaderStep` constructor is now public, which allows capturing its state lazily, which is helpful for setting defaults based on `ratchetFrom`. ## [1.32.0] - 2020-06-01 ### Added diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index ceb8f5ec43..439bd08867 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -3,6 +3,12 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`). ## [Unreleased] +### Added +* If you use `ratchetFrom` and `licenseHeader`, the year in your license header will now be automatically kept up-to-date for changed files. For example, if the current year is 2020: ([#593](https://github.com/diffplug/spotless/pull/593)) + * `/** Copyright 2020 */` -> unchanged + * `/** Copyright 1990 */` -> `/** Copyright 1990-2020 */` + * `/** Copyright 1990-1993 */` -> `/** Copyright 1990-2020 */` + * You can disable this behavior with `licenseHeader(...).updateYearWithLatest(false)`, or you can enable it without using `ratchetFrom` by using `updateYearWithLatest(true)` (not recommended). ## [4.1.0] - 2020-06-01 ### Added diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index d6972bdd2b..3fa531ca85 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -543,32 +543,15 @@ to true. ## License header options -If the license header (specified with `licenseHeader` or `licenseHeaderFile`) contains `$YEAR` or `$today.year`, then that token will be replaced with the current 4-digit year. For example, if Spotless is launched in 2017, then `/* Licensed under Apache-2.0 $YEAR. */` will produce `/* Licensed under Apache-2.0 2017. */` +If the license header (specified with `licenseHeader` or `licenseHeaderFile`) contains `$YEAR` or `$today.year`, then that token will be replaced with the current 4-digit year. For example, if Spotless is launched in 2020, then `/* Licensed under Apache-2.0 $YEAR. */` will produce `/* Licensed under Apache-2.0 2020. */` -The `licenseHeader` and `licenseHeaderFile` steps will generate license headers with automatic years according to the following rules: -* A generated license header will be updated with the current year when - * the generated license header is missing - * the generated license header is not formatted correctly -* A generated license header will _not_ be updated when - * a single year is already present, e.g. - `/* Licensed under Apache-2.0 1990. */` - * a year range is already present, e.g. - `/* Licensed under Apache-2.0 1990-2003. */` - * the `$YEAR` token is otherwise missing +Once a file's license header has a valid year, whether it is a year (`2020`) or a year range (`2017-2020`), it will not be changed. If you want the date to be updated when it changes, enable the [`ratchetFrom` functionality](#ratchet), and the year will be automatically set to today's year according to the following table (assuming the current year is 2020): -The separator for the year range defaults to the hyphen character, e.g `1990-2003`, but can be customized with the `yearSeparator` property. +* No license header -> `2020` +* `2017` -> `2017-2020` +* `2017-2019` -> `2017-2020` -For instance, the following configuration treats `1990, 2003` as a valid year range. - -```gradle -spotless { - java { - licenseHeader('Licensed under Apache-2.0 $YEAR').yearSeparator(', ') - } -} -``` - -To update the copyright notice only for changed files, use the [`ratchetFrom` functionality](#ratchet). +See the [javadoc](https://javadoc.io/static/com.diffplug.spotless/spotless-plugin-gradle/4.1.0/com/diffplug/gradle/spotless/FormatExtension.LicenseHeaderConfig.html) for a complete listing of options. diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java index 01608acc01..cfb259dedd 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java @@ -408,6 +408,11 @@ public void indentWithTabs() { addStep(IndentStep.Type.TAB.create()); } + /** + * Created by {@link FormatExtension#licenseHeader(String, String)} or {@link FormatExtension#licenseHeaderFile(Object, String)}. + * For most language-specific formats (e.g. java, scala, etc.) you can omit the second `delimiter` argument, because it is supplied + * automatically ({@link HasBuiltinDelimiterForLicense}). + */ public abstract class LicenseHeaderConfig { String delimiter; String yearSeparator = LicenseHeaderStep.defaultYearDelimiter(); @@ -459,7 +464,7 @@ FormatterStep createStep() { } } - public class LicenseStringHeaderConfig extends LicenseHeaderConfig { + private class LicenseStringHeaderConfig extends LicenseHeaderConfig { private String header; LicenseStringHeaderConfig(String delimiter, String header) { @@ -473,7 +478,7 @@ protected String licenseHeader() { } } - public class LicenseFileHeaderConfig extends LicenseHeaderConfig { + private class LicenseFileHeaderConfig extends LicenseHeaderConfig { private Object headerFile; LicenseFileHeaderConfig(String delimiter, Object headerFile) { diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 46047a566a..e2874d243b 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] +### Fixed +* `licenseHeader` is now more robust when parsing years from existing license headers. ([#593](https://github.com/diffplug/spotless/pull/593)) ## [1.31.2] - 2020-06-01 ### Fixed From a2bb01a7764fb721419ea4189b90c67b5283203c Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Tue, 2 Jun 2020 17:59:28 -0700 Subject: [PATCH 08/10] Cleanup the license test. --- .../generic/LicenseHeaderStepTest.java | 115 +++++++++--------- 1 file changed, 56 insertions(+), 59 deletions(-) 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 554b0473a4..65c6e61989 100644 --- a/testlib/src/test/java/com/diffplug/spotless/generic/LicenseHeaderStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/generic/LicenseHeaderStepTest.java @@ -38,12 +38,12 @@ public class LicenseHeaderStepTest extends ResourceHarness { private static final String KEY_LICENSE_WITH_PLACEHOLDER = "license/LicenseHeaderWithPlaceholder"; private static final String KEY_FILE_WITH_LICENSE_AND_PLACEHOLDER = "license/FileWithLicenseHeaderAndPlaceholder.test"; // Licenses to test $YEAR token replacement - private static final String LICENSE_HEADER_YEAR = "This is a fake license, $YEAR. ACME corp."; + private static final String HEADER_WITH_YEAR = "This is a fake license, $YEAR. ACME corp."; // License to test $today.year token replacement - private static final String LICENSE_HEADER_YEAR_INTELLIJ_TOKEN = "This is a fake license, $today.year. ACME corp."; + private static final String HEADER_WITH_YEAR_INTELLIJ = "This is a fake license, $today.year. ACME corp."; // Special case where the characters immediately before and after the year token are the same, // start position of the second part might overlap the end position of the first part. - private static final String LICENSE_HEADER_YEAR_VARIANT = "This is a fake license. Copyright $YEAR ACME corp."; + private static final String HEADER_WITH_YEAR_VARIANT = "This is a fake license. Copyright $YEAR ACME corp."; // If this constant changes, don't forget to change the similarly-named one in // plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java as well @@ -63,90 +63,78 @@ public void fromFile() throws Throwable { @Test public void should_apply_license_containing_YEAR_token() throws Throwable { - FormatterStep step = LicenseHeaderStep.createFromFile(createLicenseWith(LICENSE_HEADER_YEAR), StandardCharsets.UTF_8, LICENSE_HEADER_DELIMITER); - - StepHarness.forStep(step) - .test(getTestResource(KEY_FILE_WITHOUT_LICENSE), fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())) - .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())) - .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "2003")) - .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990-2015")) - .test(fileWithLicenseContaining("Something before license.*/\n/* \n * " + LICENSE_HEADER_YEAR, "2003"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())) - .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR + "\n **/\n/* Something after license.", "2003"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, "2003")) - .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "not a year"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())); - + StepHarness.forStep(LicenseHeaderStep.createFromHeader(licenseWith(HEADER_WITH_YEAR), LICENSE_HEADER_DELIMITER)) + .test(getTestResource(KEY_FILE_WITHOUT_LICENSE), fileContainingYear(HEADER_WITH_YEAR, currentYear())) + .testUnaffected(fileContainingYear(HEADER_WITH_YEAR, currentYear())) + .testUnaffected(fileContainingYear(HEADER_WITH_YEAR, "2003")) + .testUnaffected(fileContainingYear(HEADER_WITH_YEAR, "1990-2015")) + .test(fileContainingYear("Something before license.*/\n/* \n * " + HEADER_WITH_YEAR, "2003"), fileContainingYear(HEADER_WITH_YEAR, currentYear())) + .test(fileContainingYear(HEADER_WITH_YEAR + "\n **/\n/* Something after license.", "2003"), fileContainingYear(HEADER_WITH_YEAR, "2003")) + .test(fileContainingYear(HEADER_WITH_YEAR, "not a year"), fileContainingYear(HEADER_WITH_YEAR, currentYear())); // Check with variant - step = LicenseHeaderStep.createFromFile(createLicenseWith(LICENSE_HEADER_YEAR_VARIANT), StandardCharsets.UTF_8, LICENSE_HEADER_DELIMITER); - - StepHarness.forStep(step) - .test(getTestResource(KEY_FILE_WITHOUT_LICENSE), fileWithLicenseContaining(LICENSE_HEADER_YEAR_VARIANT, currentYear())) - .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR_VARIANT, currentYear())) - .test(fileWithLicenseContaining("This is a fake license. Copyright "), fileWithLicenseContaining(LICENSE_HEADER_YEAR_VARIANT, currentYear())) - .test(fileWithLicenseContaining(" ACME corp."), fileWithLicenseContaining(LICENSE_HEADER_YEAR_VARIANT, currentYear())) - .test(fileWithLicenseContaining("This is a fake license. Copyright ACME corp."), fileWithLicenseContaining(LICENSE_HEADER_YEAR_VARIANT, currentYear())) - .test(fileWithLicenseContaining("This is a fake license. CopyrightACME corp."), fileWithLicenseContaining(LICENSE_HEADER_YEAR_VARIANT, currentYear())); + StepHarness.forStep(LicenseHeaderStep.createFromHeader(licenseWith(HEADER_WITH_YEAR_VARIANT), LICENSE_HEADER_DELIMITER)) + .test(getTestResource(KEY_FILE_WITHOUT_LICENSE), fileContainingYear(HEADER_WITH_YEAR_VARIANT, currentYear())) + .testUnaffected(fileContainingYear(HEADER_WITH_YEAR_VARIANT, currentYear())) + .test(fileContaining("This is a fake license. Copyright "), fileContainingYear(HEADER_WITH_YEAR_VARIANT, currentYear())) + .test(fileContaining(" ACME corp."), fileContainingYear(HEADER_WITH_YEAR_VARIANT, currentYear())) + .test(fileContaining("This is a fake license. Copyright ACME corp."), fileContainingYear(HEADER_WITH_YEAR_VARIANT, currentYear())) + .test(fileContaining("This is a fake license. CopyrightACME corp."), fileContainingYear(HEADER_WITH_YEAR_VARIANT, currentYear())); //Check when token is of the format $today.year - step = LicenseHeaderStep.createFromFile(createLicenseWith(LICENSE_HEADER_YEAR_INTELLIJ_TOKEN), StandardCharsets.UTF_8, LICENSE_HEADER_DELIMITER); + StepHarness.forStep(LicenseHeaderStep.createFromHeader(licenseWith(HEADER_WITH_YEAR_INTELLIJ), LICENSE_HEADER_DELIMITER)) + .test(fileContaining(HEADER_WITH_YEAR_INTELLIJ), fileWithLicenseContaining(HEADER_WITH_YEAR_INTELLIJ, currentYear(), "$today.year")); + } - StepHarness.forStep(step) - .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR_INTELLIJ_TOKEN), fileWithLicenseContaining(LICENSE_HEADER_YEAR_INTELLIJ_TOKEN, currentYear(), "$today.year")); + private String fileWithLicenseContaining(String license, String yearContent, String token) throws IOException { + return getTestResource(KEY_FILE_WITH_LICENSE_AND_PLACEHOLDER).replace("__LICENSE_PLACEHOLDER__", license).replace(token, yearContent); } @Test public void updateYearWithLatest() throws Throwable { - FormatterStep step = LicenseHeaderStep.createFromFile(createLicenseWith(LICENSE_HEADER_YEAR), StandardCharsets.UTF_8, LICENSE_HEADER_DELIMITER, "-", true); + LicenseHeaderStep stepState = new LicenseHeaderStep(licenseWith(HEADER_WITH_YEAR), LICENSE_HEADER_DELIMITER, "-", true); + FormatterStep step = FormatterStep.create(LicenseHeaderStep.name(), stepState, s -> s::format); StepHarness.forStep(step) - .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())) - .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "2003"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, "2003-" + currentYear())) - .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990-2015"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990-" + currentYear())); + .testUnaffected(fileContainingYear(HEADER_WITH_YEAR, currentYear())) + .test(fileContainingYear(HEADER_WITH_YEAR, "2003"), fileContainingYear(HEADER_WITH_YEAR, "2003-" + currentYear())) + .test(fileContainingYear(HEADER_WITH_YEAR, "1990-2015"), fileContainingYear(HEADER_WITH_YEAR, "1990-" + currentYear())); } @Test public void should_apply_license_containing_YEAR_token_with_non_default_year_separator() throws Throwable { - FormatterStep step = LicenseHeaderStep.createFromFile(createLicenseWith(LICENSE_HEADER_YEAR), StandardCharsets.UTF_8, LICENSE_HEADER_DELIMITER, ", "); - - StepHarness.forStep(step) - .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990, 2015")) - .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990-2015"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990, 2015")); + StepHarness.forStep(LicenseHeaderStep.createFromHeader(licenseWith(HEADER_WITH_YEAR), LICENSE_HEADER_DELIMITER, ", ")) + .testUnaffected(fileContainingYear(HEADER_WITH_YEAR, "1990, 2015")) + .test(fileContainingYear(HEADER_WITH_YEAR, "1990-2015"), fileContainingYear(HEADER_WITH_YEAR, "1990, 2015")); } @Test public void should_apply_license_containing_YEAR_token_with_special_character_in_year_separator() throws Throwable { - FormatterStep step = LicenseHeaderStep.createFromFile(createLicenseWith(LICENSE_HEADER_YEAR), StandardCharsets.UTF_8, LICENSE_HEADER_DELIMITER, "("); - - StepHarness.forStep(step) - .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990(2015")) - .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990-2015"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990(2015")); + StepHarness.forStep(LicenseHeaderStep.createFromHeader(licenseWith(HEADER_WITH_YEAR), LICENSE_HEADER_DELIMITER, "(")) + .testUnaffected(fileContainingYear(HEADER_WITH_YEAR, "1990(2015")) + .test(fileContainingYear(HEADER_WITH_YEAR, "1990-2015"), fileContainingYear(HEADER_WITH_YEAR, "1990(2015")); } @Test public void should_apply_license_containing_YEAR_token_with_custom_separator() throws Throwable { - FormatterStep step = LicenseHeaderStep.createFromFile(createLicenseWith(LICENSE_HEADER_YEAR), StandardCharsets.UTF_8, LICENSE_HEADER_DELIMITER); - - StepHarness.forStep(step) - .test(getTestResource(KEY_FILE_WITHOUT_LICENSE), fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())) - .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())) - .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "2003")) - .testUnaffected(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "1990-2015")) - .test(fileWithLicenseContaining(LICENSE_HEADER_YEAR, "not a year"), fileWithLicenseContaining(LICENSE_HEADER_YEAR, currentYear())); + StepHarness.forStep(LicenseHeaderStep.createFromHeader(licenseWith(HEADER_WITH_YEAR), LICENSE_HEADER_DELIMITER)) + .test(getTestResource(KEY_FILE_WITHOUT_LICENSE), fileContainingYear(HEADER_WITH_YEAR, currentYear())) + .testUnaffected(fileContainingYear(HEADER_WITH_YEAR, currentYear())) + .testUnaffected(fileContainingYear(HEADER_WITH_YEAR, "2003")) + .testUnaffected(fileContainingYear(HEADER_WITH_YEAR, "1990-2015")) + .test(fileContainingYear(HEADER_WITH_YEAR, "not a year"), fileContainingYear(HEADER_WITH_YEAR, currentYear())); } - private File createLicenseWith(String contents) throws IOException { - return createTestFile(KEY_LICENSE_WITH_PLACEHOLDER, c -> c.replace("__LICENSE_PLACEHOLDER__", contents)); + private String licenseWith(String contents) throws IOException { + return getTestResource(KEY_LICENSE_WITH_PLACEHOLDER).replace("__LICENSE_PLACEHOLDER__", contents); } - private String fileWithLicenseContaining(String license) throws IOException { - return fileWithLicenseContaining(license, ""); + private String fileContaining(String license) throws IOException { + return fileContainingYear(license, ""); } - private String fileWithLicenseContaining(String license, String yearContent) throws IOException { + private String fileContainingYear(String license, String yearContent) throws IOException { return getTestResource(KEY_FILE_WITH_LICENSE_AND_PLACEHOLDER).replace("__LICENSE_PLACEHOLDER__", license).replace("$YEAR", yearContent); } - private String fileWithLicenseContaining(String license, String yearContent, String token) throws IOException { - return getTestResource(KEY_FILE_WITH_LICENSE_AND_PLACEHOLDER).replace("__LICENSE_PLACEHOLDER__", license).replace(token, yearContent); - } - private String currentYear() { return String.valueOf(YearMonth.now().getYear()); } @@ -183,6 +171,8 @@ public void equality() { new SerializableEqualityTester() { String header = "LICENSE"; String delimiter = "package"; + String yearSep = "-"; + boolean updateYearWithLatest = false; @Override protected void setupTest(API api) { @@ -191,16 +181,23 @@ protected void setupTest(API api) { delimiter = "crate"; api.areDifferentThan(); - header = "APACHE"; + header = "APACHE $YEAR"; api.areDifferentThan(); delimiter = "package"; api.areDifferentThan(); + + yearSep = " - "; + api.areDifferentThan(); + + updateYearWithLatest = true; + api.areDifferentThan(); } @Override protected FormatterStep create() { - return LicenseHeaderStep.createFromHeader(header, delimiter); + LicenseHeaderStep stepState = new LicenseHeaderStep(header, delimiter, yearSep, updateYearWithLatest); + return FormatterStep.create(LicenseHeaderStep.name(), stepState, s -> s::format); } }.testEquals(); } From babc47a3a9500a176663b96685cb93536b726141 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Tue, 2 Jun 2020 22:53:48 -0700 Subject: [PATCH 09/10] Fix test broken by our new, more resilient license parser. --- .../java/com/diffplug/gradle/spotless/KotlinExtensionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/KotlinExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/KotlinExtensionTest.java index e1661e1ee2..0d69108f7c 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/KotlinExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/KotlinExtensionTest.java @@ -193,7 +193,7 @@ public void testWithNonStandardYearSeparator() throws IOException { matcher.startsWith("// License Header 2012, 2014"); }); assertFile("src/main/kotlin/test2.kt").matches(matcher -> { - matcher.startsWith(HEADER_WITH_YEAR.replace("$YEAR", String.valueOf(YearMonth.now().getYear()))); + matcher.startsWith("// License Header 2012, 2014"); }); } From e318a14afade887423b91a4c62e7e2f11bc96a51 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Tue, 2 Jun 2020 23:14:41 -0700 Subject: [PATCH 10/10] Fix a JDK11-only test broken by our new, more resilient license parser. --- .../java/com/diffplug/gradle/spotless/KotlinExtensionTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/KotlinExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/KotlinExtensionTest.java index 0d69108f7c..39137fc06e 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/KotlinExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/KotlinExtensionTest.java @@ -18,7 +18,6 @@ import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; -import java.time.YearMonth; import org.gradle.testkit.runner.BuildResult; import org.junit.Test; @@ -223,7 +222,7 @@ public void testWithNonStandardYearSeparatorKtfmt() throws IOException { matcher.startsWith("// License Header 2012, 2014"); }); assertFile("src/main/kotlin/test2.kt").matches(matcher -> { - matcher.startsWith(HEADER_WITH_YEAR.replace("$YEAR", String.valueOf(YearMonth.now().getYear()))); + matcher.startsWith("// License Header 2012, 2014"); }); } }