Skip to content

Commit 6a41930

Browse files
committed
add a $YEAR as a supported variable in license
This token is replaced by the current year in the following cases: * the license is missing * the license in not formatted * the $YEAR is not like 1990 or 1990-2005
1 parent 7401807 commit 6a41930

File tree

6 files changed

+162
-1
lines changed

6 files changed

+162
-1
lines changed

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

+28-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.io.Serializable;
2121
import java.nio.charset.Charset;
2222
import java.nio.file.Files;
23+
import java.time.YearMonth;
2324
import java.util.Objects;
2425
import java.util.regex.Matcher;
2526
import java.util.regex.Pattern;
@@ -35,6 +36,11 @@ public final class LicenseHeaderStep implements Serializable {
3536

3637
private final String licenseHeader;
3738
private final Pattern delimiterPattern;
39+
private Pattern yearMatcherPattern;
40+
private boolean hasYearToken;
41+
private String licenseHeaderBeforeYEARToken;
42+
private String licenseHeaderAfterYEARToken;
43+
private String licenseHeaderWithYEARTokenReplaced;
3844

3945
/** Creates a FormatterStep which forces the start of each file to match a license header. */
4046
public static FormatterStep createFromHeader(String licenseHeader, String delimiter) {
@@ -74,6 +80,14 @@ private LicenseHeaderStep(String licenseHeader, String delimiter) {
7480
}
7581
this.licenseHeader = licenseHeader;
7682
this.delimiterPattern = Pattern.compile('^' + delimiter, Pattern.UNIX_LINES | Pattern.MULTILINE);
83+
hasYearToken = licenseHeader.contains("$YEAR");
84+
if (hasYearToken) {
85+
int yearTokenIndex = licenseHeader.indexOf("$YEAR");
86+
licenseHeaderBeforeYEARToken = licenseHeader.substring(0, yearTokenIndex);
87+
licenseHeaderAfterYEARToken = licenseHeader.substring(yearTokenIndex + 5, licenseHeader.length());
88+
licenseHeaderWithYEARTokenReplaced = licenseHeader.replace("$YEAR", String.valueOf(YearMonth.now().getYear()));
89+
this.yearMatcherPattern = Pattern.compile("[0-9]{4}(-[0-9]{4})?");
90+
}
7791
}
7892

7993
/** Reads the license file from the given file. */
@@ -87,7 +101,14 @@ public String format(String raw) {
87101
if (!matcher.find()) {
88102
throw new IllegalArgumentException("Unable to find delimiter regex " + delimiterPattern);
89103
} else {
90-
if (matcher.start() == licenseHeader.length() && raw.startsWith(licenseHeader)) {
104+
if (hasYearToken) {
105+
if (matchesLicenseWithYearToken(raw, matcher)) {
106+
//that means we have the license like `licenseHeaderBeforeYEARToken 1990-2015 licenseHeaderAfterYEARToken`
107+
return raw;
108+
} else {
109+
return licenseHeaderWithYEARTokenReplaced + raw.substring(matcher.start());
110+
}
111+
} else if (matcher.start() == licenseHeader.length() && raw.startsWith(licenseHeader)) {
91112
// if no change is required, return the raw string without
92113
// creating any other new strings for maximum performance
93114
return raw;
@@ -97,4 +118,10 @@ public String format(String raw) {
97118
}
98119
}
99120
}
121+
122+
private boolean matchesLicenseWithYearToken(String raw, Matcher matcher) {
123+
int startOfTheSecondPart = raw.indexOf(licenseHeaderAfterYEARToken);
124+
return (raw.startsWith(licenseHeaderBeforeYEARToken) && startOfTheSecondPart + licenseHeaderAfterYEARToken.length() == matcher.start())
125+
&& yearMatcherPattern.matcher(raw.substring(licenseHeaderBeforeYEARToken.length(), startOfTheSecondPart)).matches();
126+
}
100127
}

plugin-gradle/README.md

+26
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,32 @@ spotless {
273273
}
274274
```
275275

276+
<a name="license-header"></a>
277+
278+
## License header options
279+
280+
The license header can contains a `$YEAR` variable that will be replaced by the current year.
281+
282+
For example:
283+
```
284+
/* Licensed under Apache-2.0 $YEAR. */
285+
```
286+
will produce
287+
```
288+
/* Licensed under Apache-2.0 2017. */
289+
```
290+
if build is launched in 2017
291+
292+
293+
The step will change the license according to the following rules
294+
* It replace the license using the current year when
295+
* The license is missing
296+
* The license is not formatted correctly
297+
* It will *not* replace the license when
298+
* The year variable is already present and is a single year, e.g. `/* Licensed under Apache-2.0 1990. */`
299+
* The year variable is already present and is a year span, e.g. `/* Licensed under Apache-2.0 1990-2003. */`
300+
301+
276302
<a name="custom"></a>
277303

278304
## Custom rules

testlib/src/test/java/com/diffplug/spotless/generic/LicenseHeaderStepTest.java

+28
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,27 @@
1616
package com.diffplug.spotless.generic;
1717

1818
import java.io.File;
19+
import java.io.IOException;
1920
import java.nio.charset.StandardCharsets;
21+
import java.time.YearMonth;
2022

2123
import org.junit.Assert;
2224
import org.junit.Test;
2325

2426
import com.diffplug.spotless.FormatterStep;
2527
import com.diffplug.spotless.ResourceHarness;
2628
import com.diffplug.spotless.SerializableEqualityTester;
29+
import com.diffplug.spotless.StepHarness;
2730

2831
public class LicenseHeaderStepTest extends ResourceHarness {
2932
private static final String KEY_LICENSE = "license/TestLicense";
3033
private static final String KEY_FILE_NOTAPPLIED = "license/MissingLicense.test";
3134
private static final String KEY_FILE_APPLIED = "license/HasLicense.test";
35+
private static final String KEY_LICENSE_WITH_YEAR = "license/TestLicencseWithYear";
36+
private static final String KEY_FILE_WITHOUT_LICENSE = "license/MissLicenseWithYear.test";
37+
private static final String KEY_FILE_WITH_PREVIOUS_YEAR = "license/LicenseWithPreviousYear.test";
38+
private static final String KEY_FILE_WITH_PREVIOUS_YEARS = "license/LicenseWithPreviousYears.test";
39+
private static final String KEY_FILE_WITH_CURRENT_YEAR = "license/LicenseWithYear.test";
3240

3341
// If this constant changes, don't forget to change the similarly-named one in
3442
// plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java as well
@@ -46,6 +54,26 @@ public void fromFile() throws Throwable {
4654
assertOnResources(step, KEY_FILE_NOTAPPLIED, KEY_FILE_APPLIED);
4755
}
4856

57+
@Test
58+
public void should_apply_license_containing_YEAR_variable() throws Throwable {
59+
FormatterStep step = LicenseHeaderStep.createFromFile(createTestFile(KEY_LICENSE_WITH_YEAR), StandardCharsets.UTF_8, LICENSE_HEADER_DELIMITER);
60+
61+
StepHarness.forStep(step)
62+
.test(getTestResource(KEY_FILE_WITHOUT_LICENSE), getFileContentWithYEAR(currentYear()))
63+
.testUnaffected(getFileContentWithYEAR(currentYear()))
64+
.testUnaffected(getFileContentWithYEAR("2003"))
65+
.testUnaffected(getFileContentWithYEAR("1990-2015"))
66+
.test(getFileContentWithYEAR("not a year"), getFileContentWithYEAR(currentYear()));
67+
}
68+
69+
private String getFileContentWithYEAR(String year) throws IOException {
70+
return getTestResource(KEY_FILE_WITH_CURRENT_YEAR).replace("__YEAR_to_replace_in_tests__", year);
71+
}
72+
73+
private String currentYear() {
74+
return String.valueOf(YearMonth.now().getYear());
75+
}
76+
4977
@Test
5078
public void efficient() throws Throwable {
5179
FormatterStep step = LicenseHeaderStep.createFromHeader("LicenseHeader\n", "contentstart");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (C) __YEAR_to_replace_in_tests__. ACME corp.
3+
* This library is free software; you can redistribute it and/or modify it under the terms
4+
* of the GNU Lesser General Public License as published by the Free Software Foundation
5+
* version 2.1 of the License.
6+
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
7+
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8+
* See the GNU Lesser General Public License for more details.
9+
* You should have received a copy of the GNU Lesser General Public License along with this
10+
* program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
11+
* Floor, Boston, MA 02110-1301, USA.
12+
**/
13+
package com.acme;
14+
15+
import java.util.function.Function;
16+
17+
18+
public class Java8Test {
19+
public void doStuff() throws Exception {
20+
Function<String, Integer> example = Integer::parseInt;
21+
example.andThen(val -> {
22+
return val + 2;
23+
} );
24+
SimpleEnum val = SimpleEnum.A;
25+
switch (val) {
26+
case A:
27+
break;
28+
case B:
29+
break;
30+
case C:
31+
break;
32+
default:
33+
throw new Exception();
34+
}
35+
}
36+
37+
public enum SimpleEnum {
38+
A, B, C;
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.acme;
2+
3+
import java.util.function.Function;
4+
5+
6+
public class Java8Test {
7+
public void doStuff() throws Exception {
8+
Function<String, Integer> example = Integer::parseInt;
9+
example.andThen(val -> {
10+
return val + 2;
11+
} );
12+
SimpleEnum val = SimpleEnum.A;
13+
switch (val) {
14+
case A:
15+
break;
16+
case B:
17+
break;
18+
case C:
19+
break;
20+
default:
21+
throw new Exception();
22+
}
23+
}
24+
25+
public enum SimpleEnum {
26+
A, B, C;
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright (C) $YEAR. ACME corp.
3+
* This library is free software; you can redistribute it and/or modify it under the terms
4+
* of the GNU Lesser General Public License as published by the Free Software Foundation
5+
* version 2.1 of the License.
6+
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
7+
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8+
* See the GNU Lesser General Public License for more details.
9+
* You should have received a copy of the GNU Lesser General Public License along with this
10+
* program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
11+
* Floor, Boston, MA 02110-1301, USA.
12+
**/

0 commit comments

Comments
 (0)