Skip to content

Commit db3ae46

Browse files
authored
Issue 650 872 multi license headers (#990)
2 parents db06b28 + 2cdddc3 commit db3ae46

File tree

9 files changed

+201
-16
lines changed

9 files changed

+201
-16
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
1212
## [Unreleased]
1313

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

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2016-2021 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless;
17+
18+
import java.io.File;
19+
import java.util.Objects;
20+
import java.util.regex.Matcher;
21+
import java.util.regex.Pattern;
22+
23+
import javax.annotation.Nullable;
24+
25+
final class FilterByContentPatternFormatterStep implements FormatterStep {
26+
private final FormatterStep delegateStep;
27+
final Pattern contentPattern;
28+
29+
FilterByContentPatternFormatterStep(FormatterStep delegateStep, String contentPattern) {
30+
this.delegateStep = Objects.requireNonNull(delegateStep);
31+
this.contentPattern = Pattern.compile(Objects.requireNonNull(contentPattern));
32+
}
33+
34+
@Override
35+
public String getName() {
36+
return delegateStep.getName();
37+
}
38+
39+
@Override
40+
public @Nullable String format(String raw, File file) throws Exception {
41+
Objects.requireNonNull(raw, "raw");
42+
Objects.requireNonNull(file, "file");
43+
44+
Matcher matcher = contentPattern.matcher(raw);
45+
46+
if (matcher.find()) {
47+
return delegateStep.format(raw, file);
48+
} else {
49+
return raw;
50+
}
51+
}
52+
53+
@Override
54+
public boolean equals(Object o) {
55+
if (this == o) {
56+
return true;
57+
}
58+
if (o == null || getClass() != o.getClass()) {
59+
return false;
60+
}
61+
FilterByContentPatternFormatterStep that = (FilterByContentPatternFormatterStep) o;
62+
return Objects.equals(delegateStep, that.delegateStep) &&
63+
Objects.equals(contentPattern.pattern(), that.contentPattern.pattern());
64+
}
65+
66+
@Override
67+
public int hashCode() {
68+
return Objects.hash(delegateStep, contentPattern.pattern());
69+
}
70+
71+
private static final long serialVersionUID = 1L;
72+
}

lib/src/main/java/com/diffplug/spotless/FormatterStep.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ public interface FormatterStep extends Serializable {
4545
*/
4646
public @Nullable String format(String rawUnix, File file) throws Exception;
4747

48+
/**
49+
* Returns a new FormatterStep which will only apply its changes
50+
* to files which pass the given filter.
51+
*
52+
* @param contentPattern
53+
* java regular expression used to filter out files which content doesn't contain pattern
54+
* @return FormatterStep
55+
*/
56+
public default FormatterStep filterByContentPattern(String contentPattern) {
57+
return new FilterByContentPatternFormatterStep(this, contentPattern);
58+
}
59+
4860
/**
4961
* Returns a new FormatterStep which will only apply its changes
5062
* to files which pass the given filter.

lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,54 +51,72 @@ public static LicenseHeaderStep headerDelimiter(String header, String delimiter)
5151
}
5252

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

57+
final String name;
58+
final @Nullable String contentPattern;
5759
final ThrowingEx.Supplier<String> headerLazy;
5860
final String delimiter;
5961
final String yearSeparator;
6062
final Supplier<YearMode> yearMode;
6163

62-
private LicenseHeaderStep(ThrowingEx.Supplier<String> headerLazy, String delimiter, String yearSeparator, Supplier<YearMode> yearMode) {
64+
private LicenseHeaderStep(String name, String contentPattern, ThrowingEx.Supplier<String> headerLazy, String delimiter, String yearSeparator, Supplier<YearMode> yearMode) {
65+
this.name = sanitizeName(name);
66+
this.contentPattern = sanitizeContentPattern(contentPattern);
6367
this.headerLazy = Objects.requireNonNull(headerLazy);
6468
this.delimiter = Objects.requireNonNull(delimiter);
6569
this.yearSeparator = Objects.requireNonNull(yearSeparator);
6670
this.yearMode = Objects.requireNonNull(yearMode);
6771
}
6872

73+
public String getName() {
74+
return name;
75+
}
76+
77+
public LicenseHeaderStep withName(String name) {
78+
return new LicenseHeaderStep(name, contentPattern, headerLazy, delimiter, yearSeparator, yearMode);
79+
}
80+
81+
public LicenseHeaderStep withContentPattern(String contentPattern) {
82+
return new LicenseHeaderStep(name, contentPattern, headerLazy, delimiter, yearSeparator, yearMode);
83+
}
84+
6985
public LicenseHeaderStep withHeaderString(String header) {
7086
return withHeaderLazy(() -> header);
7187
}
7288

7389
public LicenseHeaderStep withHeaderLazy(ThrowingEx.Supplier<String> headerLazy) {
74-
return new LicenseHeaderStep(headerLazy, delimiter, yearSeparator, yearMode);
90+
return new LicenseHeaderStep(name, contentPattern, headerLazy, delimiter, yearSeparator, yearMode);
7591
}
7692

7793
public LicenseHeaderStep withDelimiter(String delimiter) {
78-
return new LicenseHeaderStep(headerLazy, delimiter, yearSeparator, yearMode);
94+
return new LicenseHeaderStep(name, contentPattern, headerLazy, delimiter, yearSeparator, yearMode);
7995
}
8096

8197
public LicenseHeaderStep withYearSeparator(String yearSeparator) {
82-
return new LicenseHeaderStep(headerLazy, delimiter, yearSeparator, yearMode);
98+
return new LicenseHeaderStep(name, contentPattern, headerLazy, delimiter, yearSeparator, yearMode);
8399
}
84100

85101
public LicenseHeaderStep withYearMode(YearMode yearMode) {
86102
return withYearModeLazy(() -> yearMode);
87103
}
88104

89105
public LicenseHeaderStep withYearModeLazy(Supplier<YearMode> yearMode) {
90-
return new LicenseHeaderStep(headerLazy, delimiter, yearSeparator, yearMode);
106+
return new LicenseHeaderStep(name, contentPattern, headerLazy, delimiter, yearSeparator, yearMode);
91107
}
92108

93109
public FormatterStep build() {
110+
FormatterStep formatterStep = null;
111+
94112
if (yearMode.get() == YearMode.SET_FROM_GIT) {
95-
return FormatterStep.createNeverUpToDateLazy(LicenseHeaderStep.name(), () -> {
113+
formatterStep = FormatterStep.createNeverUpToDateLazy(name, () -> {
96114
boolean updateYear = false; // doesn't matter
97115
Runtime runtime = new Runtime(headerLazy.get(), delimiter, yearSeparator, updateYear);
98116
return FormatterFunc.needsFile(runtime::setLicenseHeaderYearsFromGitHistory);
99117
});
100118
} else {
101-
return FormatterStep.createLazy(LicenseHeaderStep.name(), () -> {
119+
formatterStep = FormatterStep.createLazy(name, () -> {
102120
// by default, we should update the year if the user is using ratchetFrom
103121
boolean updateYear;
104122
switch (yearMode.get()) {
@@ -115,19 +133,50 @@ public FormatterStep build() {
115133
return new Runtime(headerLazy.get(), delimiter, yearSeparator, updateYear);
116134
}, step -> step::format);
117135
}
136+
137+
if (contentPattern == null) {
138+
return formatterStep;
139+
}
140+
141+
return formatterStep.filterByContentPattern(contentPattern);
142+
}
143+
144+
private String sanitizeName(String name) {
145+
if (name == null) {
146+
return DEFAULT_NAME_PREFIX;
147+
}
148+
149+
name = name.trim();
150+
151+
if (Objects.equals(DEFAULT_NAME_PREFIX, name) || name.startsWith(DEFAULT_NAME_PREFIX)) {
152+
return name;
153+
}
154+
155+
return DEFAULT_NAME_PREFIX + "-" + name;
118156
}
119157

120-
private static final String NAME = "licenseHeader";
158+
@Nullable
159+
private String sanitizeContentPattern(String contentPattern) {
160+
if (contentPattern == null) {
161+
return contentPattern;
162+
}
163+
164+
contentPattern = contentPattern.trim();
165+
166+
if (contentPattern.isEmpty()) {
167+
return null;
168+
}
169+
170+
return contentPattern;
171+
}
172+
173+
private static final String DEFAULT_NAME_PREFIX = LicenseHeaderStep.class.getName();
121174
private static final String DEFAULT_YEAR_DELIMITER = "-";
122175
private static final List<String> YEAR_TOKENS = Arrays.asList("$YEAR", "$today.year");
123176

124177
private static final SerializableFileFilter UNSUPPORTED_JVM_FILES_FILTER = SerializableFileFilter.skipFilesNamed(
125178
"package-info.java", "package-info.groovy", "module-info.java");
126179

127-
public static String name() {
128-
return NAME;
129-
}
130-
131180
public static String defaultYearDelimiter() {
132181
return DEFAULT_YEAR_DELIMITER;
133182
}

plugin-gradle/CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
55
## [Unreleased]
66

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

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,16 @@ public void encoding(String charset) {
159159
/** The files to be formatted = (target - targetExclude). */
160160
protected FileCollection target, targetExclude;
161161

162+
protected boolean isLicenseHeaderStep(FormatterStep formatterStep) {
163+
String formatterStepName = formatterStep.getName();
164+
165+
if (formatterStepName.startsWith(LicenseHeaderStep.class.getName())) {
166+
return true;
167+
}
168+
169+
return false;
170+
}
171+
162172
/**
163173
* Sets which files should be formatted. Files to be formatted = (target - targetExclude).
164174
*
@@ -410,6 +420,24 @@ public class LicenseHeaderConfig {
410420
LicenseHeaderStep builder;
411421
Boolean updateYearWithLatest = null;
412422

423+
public LicenseHeaderConfig named(String name) {
424+
String existingStepName = builder.getName();
425+
builder = builder.withName(name);
426+
int existingStepIdx = getExistingStepIdx(existingStepName);
427+
if (existingStepIdx != -1) {
428+
steps.set(existingStepIdx, createStep());
429+
} else {
430+
addStep(createStep());
431+
}
432+
return this;
433+
}
434+
435+
public LicenseHeaderConfig onlyIfContentMatches(String contentPattern) {
436+
builder = builder.withContentPattern(contentPattern);
437+
replaceStep(createStep());
438+
return this;
439+
}
440+
413441
public LicenseHeaderConfig(LicenseHeaderStep builder) {
414442
this.builder = builder;
415443
}

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GroovyExtension.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 DiffPlug
2+
* Copyright 2016-2021 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -127,7 +127,7 @@ protected void setupTask(SpotlessTask task) {
127127
// LicenseHeaderStep completely blows apart package-info.java/groovy - this common-sense check
128128
// ensures that it skips both. See https://github.com/diffplug/spotless/issues/1
129129
steps.replaceAll(step -> {
130-
if (LicenseHeaderStep.name().equals(step.getName())) {
130+
if (isLicenseHeaderStep(step)) {
131131
return step.filterByFile(LicenseHeaderStep.unsupportedJvmFilesFilter());
132132
} else {
133133
return step;

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ protected void setupTask(SpotlessTask task) {
217217
}
218218

219219
steps.replaceAll(step -> {
220-
if (LicenseHeaderStep.name().equals(step.getName())) {
220+
if (isLicenseHeaderStep(step)) {
221221
return step.filterByFile(LicenseHeaderStep.unsupportedJvmFilesFilter());
222222
} else {
223223
return step;

plugin-gradle/src/test/java/com/diffplug/gradle/spotless/LicenseHeaderTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,26 @@ void updateYearWithLatestTrue() throws IOException {
7474
testSuiteUpdateWithLatest(true);
7575
}
7676

77+
@Test
78+
void filterByContentPatternTest() throws IOException {
79+
setLicenseStep("licenseHeader('/** $YEAR */').onlyIfContentMatches('.+Test.+').updateYearWithLatest(true)");
80+
testSuiteUpdateWithLatest(true);
81+
setLicenseStep("licenseHeader('/** $YEAR */').onlyIfContentMatches('missingString').updateYearWithLatest(true)");
82+
setFile(TEST_JAVA).toContent("/** This license header should be preserved */\n" + CONTENT);
83+
gradleRunner().withArguments("spotlessApply", "--stacktrace").forwardOutput().build();
84+
assertFile(TEST_JAVA).hasContent("/** This license header should be preserved */\n" + CONTENT);
85+
setLicenseStep("licenseHeader('/** New License Header */').named('PrimaryHeaderLicense').onlyIfContentMatches('.+Test.+')");
86+
setFile(TEST_JAVA).toContent(CONTENT);
87+
gradleRunner().withArguments("spotlessApply", "--stacktrace").forwardOutput().build();
88+
assertFile(TEST_JAVA).hasContent("/** New License Header */\n" + CONTENT);
89+
String multipleLicenseHeaderConfiguration = "licenseHeader('/** Base License Header */').named('PrimaryHeaderLicense').onlyIfContentMatches('Best')\n" +
90+
"licenseHeader('/** Alternate License Header */').named('SecondaryHeaderLicense').onlyIfContentMatches('.*Test.+')";
91+
setLicenseStep(multipleLicenseHeaderConfiguration);
92+
setFile(TEST_JAVA).toContent("/** 2003 */\n" + CONTENT);
93+
gradleRunner().withArguments("spotlessApply", "--stacktrace").forwardOutput().build();
94+
assertFile(TEST_JAVA).hasContent("/** Alternate License Header */\n" + CONTENT);
95+
}
96+
7797
@Test
7898
void ratchetFrom() throws Exception {
7999
try (Git git = Git.init().setDirectory(rootFolder()).call()) {

0 commit comments

Comments
 (0)