diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md
index 9c741a0aea..d22b01559f 100644
--- a/plugin-maven/CHANGES.md
+++ b/plugin-maven/CHANGES.md
@@ -3,6 +3,7 @@
### Version 1.22.0-SNAPSHOT - TBD ([javadoc](https://diffplug.github.io/spotless/javadoc/spotless-maven-plugin/snapshot/), [snapshot](https://oss.sonatype.org/content/repositories/snapshots/com/diffplug/spotless/spotless-maven-plugin/))
* Updated default eclipse-cdt from 4.7.3a to 4.11.0 ([#390](https://github.com/diffplug/spotless/pull/390)).
+* Added `-DspotlessFiles` switch to allow targeting specific files ([#392](https://github.com/diffplug/spotless/pull/392))
### Version 1.21.1 - March 29th 2019 ([javadoc](https://diffplug.github.io/spotless/javadoc/spotless-maven-plugin/1.21.1/), [jcenter](https://bintray.com/diffplug/opensource/spotless-maven-plugin/1.21.1))
diff --git a/plugin-maven/README.md b/plugin-maven/README.md
index b1ee9d8bcd..fca10012d6 100644
--- a/plugin-maven/README.md
+++ b/plugin-maven/README.md
@@ -377,6 +377,16 @@ By default, `spotless:check` is bound to the `verify` phase. You might want to
+## Can I apply Spotless to specific files?
+
+You can target specific files by setting the `spotlessFiles` project property to a comma-separated list of file patterns:
+
+```
+cmd> mvn spotless:apply -DspotlessFiles=my/file/pattern.java,more/generic/.*-pattern.java
+```
+
+The patterns are matched using `String#matches(String)` against the absolute file path.
+
## Example configurations (from real-world projects)
- [Apache Avro](https://github.com/apache/avro/blob/8026c8ffe4ef67ab419dba73910636bf2c1a691c/lang/java/pom.xml#L307-L334)
diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java
index 0d2753ca94..f045348117 100644
--- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java
+++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java
@@ -20,6 +20,9 @@
import java.io.File;
import java.io.IOException;
import java.util.*;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.maven.plugin.AbstractMojo;
@@ -100,6 +103,9 @@ public abstract class AbstractSpotlessMojo extends AbstractMojo {
@Deprecated
private com.diffplug.spotless.maven.css.Css css;
+ @Parameter(property = "spotlessFiles")
+ private String filePatterns;
+
protected abstract void process(List files, Formatter formatter) throws MojoExecutionException;
@Override
@@ -132,7 +138,22 @@ private List collectFiles(FormatterFactory formatterFactory) throws MojoEx
String excludesString = String.join(",", excludes);
try {
- return FileUtils.getFiles(baseDir, includesString, excludesString);
+ final List files = FileUtils.getFiles(baseDir, includesString, excludesString);
+ if (filePatterns == null || filePatterns.isEmpty()) {
+ return files;
+ }
+ final String[] includePatterns = this.filePatterns.split(",");
+ final List compiledIncludePatterns = Arrays.stream(includePatterns)
+ .map(Pattern::compile)
+ .collect(Collectors.toList());
+ final Predicate shouldInclude = file -> compiledIncludePatterns
+ .stream()
+ .anyMatch(filePattern -> filePattern.matcher(file.getAbsolutePath())
+ .matches());
+ return files
+ .stream()
+ .filter(shouldInclude)
+ .collect(toList());
} catch (IOException e) {
throw new MojoExecutionException("Unable to scan file tree rooted at " + baseDir, e);
}
diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/FileLocator.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/FileLocator.java
index 0d28c68ee1..126ac4d49e 100644
--- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/FileLocator.java
+++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/FileLocator.java
@@ -46,7 +46,7 @@ public File locateFile(String path) {
} catch (ResourceNotFoundException e) {
throw new RuntimeException("Unable to locate file with path: " + path, e);
} catch (FileResourceCreationException e) {
- throw new RuntimeException("Unable to create temporaty file '" + outputFile + "' in the output directory", e);
+ throw new RuntimeException("Unable to create temporary file '" + outputFile + "' in the output directory", e);
}
}
diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/SpecificFilesTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/SpecificFilesTest.java
new file mode 100644
index 0000000000..8f1b739ad4
--- /dev/null
+++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/SpecificFilesTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ * 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.maven;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+public class SpecificFilesTest extends MavenIntegrationTest {
+ private String testFile(int number, boolean absolute) throws IOException {
+ String rel = "src/main/java/test" + number + ".java";
+ if (absolute) {
+ return rootFolder() + "/" + rel;
+ } else {
+ return rel;
+ }
+ }
+
+ private String testFile(int number) throws IOException {
+ return testFile(number, false);
+ }
+
+ private String fixture(boolean formatted) {
+ return "java/googlejavaformat/JavaCode" + (formatted ? "F" : "Unf") + "ormatted.test";
+ }
+
+ private void integration(String patterns, boolean firstFormatted, boolean secondFormatted, boolean thirdFormatted)
+ throws IOException, InterruptedException {
+
+ writePomWithJavaSteps(
+ "",
+ " src/**/java/**/*.java",
+ "",
+ "",
+ " 1.2",
+ "");
+
+ setFile(testFile(1)).toResource(fixture(false));
+ setFile(testFile(2)).toResource(fixture(false));
+ setFile(testFile(3)).toResource(fixture(false));
+
+ mavenRunner()
+ .withArguments("spotless:apply", "-DspotlessFiles=" + patterns)
+ .runNoError();
+
+ assertFile(testFile(1)).sameAsResource(fixture(firstFormatted));
+ assertFile(testFile(2)).sameAsResource(fixture(secondFormatted));
+ assertFile(testFile(3)).sameAsResource(fixture(thirdFormatted));
+ }
+
+ @Test
+ public void singleFile() throws IOException, InterruptedException {
+ integration(testFile(2, true), false, true, false);
+ }
+
+ @Test
+ public void multiFile() throws IOException, InterruptedException {
+ integration(testFile(1, true) + "," + testFile(3, true), true, false, true);
+ }
+
+ @Test
+ public void regexp() throws IOException, InterruptedException {
+ integration(".*/src/main/java/test\\(1\\|3\\).java", true, false, true);
+ }
+}