Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
## [Unreleased]

### Changed
* Added `named` option to `licenseHeader` to support alternate license header within same format (like java) ([872](https://github.com/diffplug/spotless/issues/872)).
* Added `onlyIfContentMatches` option to `licenseHeader` to skip license header application based on source file content pattern ([#650](https://github.com/diffplug/spotless/issues/650)).
* Bump jgit version ([#992](https://github.com/diffplug/spotless/pull/992)).
* jgit `5.10.0.202012080955-r` -> `5.13.0.202109080827-r`

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 2016-2021 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.spotless;

import java.io.File;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Nullable;

final class FilterByContentPatternFormatterStep implements FormatterStep {
private final FormatterStep delegateStep;
final Pattern contentPattern;

FilterByContentPatternFormatterStep(FormatterStep delegateStep, String contentPattern) {
this.delegateStep = Objects.requireNonNull(delegateStep);
this.contentPattern = Pattern.compile(Objects.requireNonNull(contentPattern));
}

@Override
public String getName() {
return delegateStep.getName();
}

@Override
public @Nullable String format(String raw, File file) throws Exception {
Objects.requireNonNull(raw, "raw");
Objects.requireNonNull(file, "file");

Matcher matcher = contentPattern.matcher(raw);

if (matcher.find()) {
return delegateStep.format(raw, file);
} else {
return raw;
}
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
FilterByContentPatternFormatterStep that = (FilterByContentPatternFormatterStep) o;
return Objects.equals(delegateStep, that.delegateStep) &&
Objects.equals(contentPattern.pattern(), that.contentPattern.pattern());
}

@Override
public int hashCode() {
return Objects.hash(delegateStep, contentPattern.pattern());
}

private static final long serialVersionUID = 1L;
}
12 changes: 12 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/FormatterStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ public interface FormatterStep extends Serializable {
*/
public @Nullable String format(String rawUnix, File file) throws Exception;

/**
* Returns a new FormatterStep which will only apply its changes
* to files which pass the given filter.
*
* @param contentPattern
* java regular expression used to filter out files which content doesn't contain pattern
* @return FormatterStep
*/
public default FormatterStep filterByContentPattern(String contentPattern) {
return new FilterByContentPatternFormatterStep(this, contentPattern);
}

/**
* Returns a new FormatterStep which will only apply its changes
* to files which pass the given filter.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,54 +51,72 @@ public static LicenseHeaderStep headerDelimiter(String header, String delimiter)
}

public static LicenseHeaderStep headerDelimiter(ThrowingEx.Supplier<String> headerLazy, String delimiter) {
return new LicenseHeaderStep(headerLazy, delimiter, DEFAULT_YEAR_DELIMITER, () -> YearMode.PRESERVE);
return new LicenseHeaderStep(null, null, headerLazy, delimiter, DEFAULT_YEAR_DELIMITER, () -> YearMode.PRESERVE);
}

final String name;
final @Nullable String contentPattern;
final ThrowingEx.Supplier<String> headerLazy;
final String delimiter;
final String yearSeparator;
final Supplier<YearMode> yearMode;

private LicenseHeaderStep(ThrowingEx.Supplier<String> headerLazy, String delimiter, String yearSeparator, Supplier<YearMode> yearMode) {
private LicenseHeaderStep(String name, String contentPattern, ThrowingEx.Supplier<String> headerLazy, String delimiter, String yearSeparator, Supplier<YearMode> yearMode) {
this.name = sanitizeName(name);
this.contentPattern = sanitizeContentPattern(contentPattern);
this.headerLazy = Objects.requireNonNull(headerLazy);
this.delimiter = Objects.requireNonNull(delimiter);
this.yearSeparator = Objects.requireNonNull(yearSeparator);
this.yearMode = Objects.requireNonNull(yearMode);
}

public String getName() {
return name;
}

public LicenseHeaderStep withName(String name) {
return new LicenseHeaderStep(name, contentPattern, headerLazy, delimiter, yearSeparator, yearMode);
}

public LicenseHeaderStep withContentPattern(String contentPattern) {
return new LicenseHeaderStep(name, contentPattern, headerLazy, delimiter, yearSeparator, yearMode);
}

public LicenseHeaderStep withHeaderString(String header) {
return withHeaderLazy(() -> header);
}

public LicenseHeaderStep withHeaderLazy(ThrowingEx.Supplier<String> headerLazy) {
return new LicenseHeaderStep(headerLazy, delimiter, yearSeparator, yearMode);
return new LicenseHeaderStep(name, contentPattern, headerLazy, delimiter, yearSeparator, yearMode);
}

public LicenseHeaderStep withDelimiter(String delimiter) {
return new LicenseHeaderStep(headerLazy, delimiter, yearSeparator, yearMode);
return new LicenseHeaderStep(name, contentPattern, headerLazy, delimiter, yearSeparator, yearMode);
}

public LicenseHeaderStep withYearSeparator(String yearSeparator) {
return new LicenseHeaderStep(headerLazy, delimiter, yearSeparator, yearMode);
return new LicenseHeaderStep(name, contentPattern, headerLazy, delimiter, yearSeparator, yearMode);
}

public LicenseHeaderStep withYearMode(YearMode yearMode) {
return withYearModeLazy(() -> yearMode);
}

public LicenseHeaderStep withYearModeLazy(Supplier<YearMode> yearMode) {
return new LicenseHeaderStep(headerLazy, delimiter, yearSeparator, yearMode);
return new LicenseHeaderStep(name, contentPattern, headerLazy, delimiter, yearSeparator, yearMode);
}

public FormatterStep build() {
FormatterStep formatterStep = null;

if (yearMode.get() == YearMode.SET_FROM_GIT) {
return FormatterStep.createNeverUpToDateLazy(LicenseHeaderStep.name(), () -> {
formatterStep = FormatterStep.createNeverUpToDateLazy(name, () -> {
boolean updateYear = false; // doesn't matter
Runtime runtime = new Runtime(headerLazy.get(), delimiter, yearSeparator, updateYear);
return FormatterFunc.needsFile(runtime::setLicenseHeaderYearsFromGitHistory);
});
} else {
return FormatterStep.createLazy(LicenseHeaderStep.name(), () -> {
formatterStep = FormatterStep.createLazy(name, () -> {
// by default, we should update the year if the user is using ratchetFrom
boolean updateYear;
switch (yearMode.get()) {
Expand All @@ -115,19 +133,50 @@ public FormatterStep build() {
return new Runtime(headerLazy.get(), delimiter, yearSeparator, updateYear);
}, step -> step::format);
}

if (contentPattern == null) {
return formatterStep;
}

return formatterStep.filterByContentPattern(contentPattern);
}

private String sanitizeName(String name) {
if (name == null) {
return DEFAULT_NAME_PREFIX;
}

name = name.trim();

if (Objects.equals(DEFAULT_NAME_PREFIX, name) || name.startsWith(DEFAULT_NAME_PREFIX)) {
return name;
}

return DEFAULT_NAME_PREFIX + "-" + name;
}

private static final String NAME = "licenseHeader";
@Nullable
private String sanitizeContentPattern(String contentPattern) {
if (contentPattern == null) {
return contentPattern;
}

contentPattern = contentPattern.trim();

if (contentPattern.isEmpty()) {
return null;
}

return contentPattern;
}

private static final String DEFAULT_NAME_PREFIX = LicenseHeaderStep.class.getName();
private static final String DEFAULT_YEAR_DELIMITER = "-";
private static final List<String> 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");

public static String name() {
return NAME;
}

public static String defaultYearDelimiter() {
return DEFAULT_YEAR_DELIMITER;
}
Expand Down
2 changes: 2 additions & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
## [Unreleased]

### Changed
* Added `named` option to `licenseHeader` to support alternate license header within same format (like java) ([872](https://github.com/diffplug/spotless/issues/872)).
* Added `onlyIfContentMatches` option to `licenseHeader` to skip license header application based on source file content pattern ([#650](https://github.com/diffplug/spotless/issues/650)).
* Bump jgit version ([#992](https://github.com/diffplug/spotless/pull/992)).
* jgit `5.10.0.202012080955-r` -> `5.13.0.202109080827-r`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,16 @@ public void encoding(String charset) {
/** The files to be formatted = (target - targetExclude). */
protected FileCollection target, targetExclude;

protected boolean isLicenseHeaderStep(FormatterStep formatterStep) {
String formatterStepName = formatterStep.getName();

if (formatterStepName.startsWith(LicenseHeaderStep.class.getName())) {
return true;
}

return false;
}

/**
* Sets which files should be formatted. Files to be formatted = (target - targetExclude).
*
Expand Down Expand Up @@ -410,6 +420,24 @@ public class LicenseHeaderConfig {
LicenseHeaderStep builder;
Boolean updateYearWithLatest = null;

public LicenseHeaderConfig named(String name) {
String existingStepName = builder.getName();
builder = builder.withName(name);
int existingStepIdx = getExistingStepIdx(existingStepName);
if (existingStepIdx != -1) {
steps.set(existingStepIdx, createStep());
} else {
addStep(createStep());
}
return this;
}

public LicenseHeaderConfig onlyIfContentMatches(String contentPattern) {
builder = builder.withContentPattern(contentPattern);
replaceStep(createStep());
return this;
}

public LicenseHeaderConfig(LicenseHeaderStep builder) {
this.builder = builder;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2020 DiffPlug
* Copyright 2016-2021 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -127,7 +127,7 @@ protected void setupTask(SpotlessTask task) {
// LicenseHeaderStep completely blows apart package-info.java/groovy - this common-sense check
// ensures that it skips both. See https://github.com/diffplug/spotless/issues/1
steps.replaceAll(step -> {
if (LicenseHeaderStep.name().equals(step.getName())) {
if (isLicenseHeaderStep(step)) {
return step.filterByFile(LicenseHeaderStep.unsupportedJvmFilesFilter());
} else {
return step;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ protected void setupTask(SpotlessTask task) {
}

steps.replaceAll(step -> {
if (LicenseHeaderStep.name().equals(step.getName())) {
if (isLicenseHeaderStep(step)) {
return step.filterByFile(LicenseHeaderStep.unsupportedJvmFilesFilter());
} else {
return step;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,26 @@ void updateYearWithLatestTrue() throws IOException {
testSuiteUpdateWithLatest(true);
}

@Test
void filterByContentPatternTest() throws IOException {
setLicenseStep("licenseHeader('/** $YEAR */').onlyIfContentMatches('.+Test.+').updateYearWithLatest(true)");
testSuiteUpdateWithLatest(true);
setLicenseStep("licenseHeader('/** $YEAR */').onlyIfContentMatches('missingString').updateYearWithLatest(true)");
setFile(TEST_JAVA).toContent("/** This license header should be preserved */\n" + CONTENT);
gradleRunner().withArguments("spotlessApply", "--stacktrace").forwardOutput().build();
assertFile(TEST_JAVA).hasContent("/** This license header should be preserved */\n" + CONTENT);
setLicenseStep("licenseHeader('/** New License Header */').named('PrimaryHeaderLicense').onlyIfContentMatches('.+Test.+')");
setFile(TEST_JAVA).toContent(CONTENT);
gradleRunner().withArguments("spotlessApply", "--stacktrace").forwardOutput().build();
assertFile(TEST_JAVA).hasContent("/** New License Header */\n" + CONTENT);
String multipleLicenseHeaderConfiguration = "licenseHeader('/** Base License Header */').named('PrimaryHeaderLicense').onlyIfContentMatches('Best')\n" +
"licenseHeader('/** Alternate License Header */').named('SecondaryHeaderLicense').onlyIfContentMatches('.*Test.+')";
setLicenseStep(multipleLicenseHeaderConfiguration);
setFile(TEST_JAVA).toContent("/** 2003 */\n" + CONTENT);
gradleRunner().withArguments("spotlessApply", "--stacktrace").forwardOutput().build();
assertFile(TEST_JAVA).hasContent("/** Alternate License Header */\n" + CONTENT);
}

@Test
void ratchetFrom() throws Exception {
try (Git git = Git.init().setDirectory(rootFolder()).call()) {
Expand Down