diff --git a/CHANGES.md b/CHANGES.md
index 05c07860d9..77fb872344 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -12,6 +12,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
## [Unreleased]
### Added
* Added support for custom JSR223 formatters ([#945](https://github.com/diffplug/spotless/pull/945))
+* Added support for formating and sorting Maven POMs ([#946](https://github.com/diffplug/spotless/pull/946))
## [2.17.0] - 2021-09-27
### Added
diff --git a/README.md b/README.md
index 5085cee272..bd83899587 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ lib('generic.EndWithNewlineStep') +'{{yes}} | {{yes}}
lib('generic.IndentStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('generic.Jsr223Step') +'{{no}} | {{yes}} | {{no}} | {{no}} |',
lib('generic.LicenseHeaderStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
-lib('generic.NativeCmdStep') +'{{no}} | {{yes}} | {{no}} | {{no}} |',
+lib('generic.NativeCmdStep') +'{{no}} | {{yes}} | {{no}} | {{no}} |',
lib('generic.ReplaceRegexStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('generic.ReplaceStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('generic.TrimTrailingWhitespaceStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
@@ -66,6 +66,7 @@ lib('kotlin.DiktatStep') +'{{yes}} | {{yes}}
lib('markdown.FreshMarkStep') +'{{yes}} | {{no}} | {{no}} | {{no}} |',
lib('npm.PrettierFormatterStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('npm.TsFmtFormatterStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
+lib('pom.SortPomStepStep') +'{{no}} | {{yes}} | {{no}} | {{no}} |',
lib('python.BlackStep') +'{{yes}} | {{no}} | {{no}} | {{no}} |',
lib('scala.ScalaFmtStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
lib('sql.DBeaverSQLFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
@@ -86,7 +87,7 @@ extra('wtp.EclipseWtpFormatterStep') +'{{yes}} | {{yes}}
| [`generic.IndentStep`](lib/src/main/java/com/diffplug/spotless/generic/IndentStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`generic.Jsr223Step`](lib/src/main/java/com/diffplug/spotless/generic/Jsr223Step.java) | :white_large_square: | :+1: | :white_large_square: | :white_large_square: |
| [`generic.LicenseHeaderStep`](lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
-| [`generic.NativeCmdStep`](lib/src/main/java/com/diffplug/spotless/generic/NativeCmdStep.java) | :white_large_square: | :+1: | :white_large_square: | :white_large_square: |
+| [`generic.NativeCmdStep`](lib/src/main/java/com/diffplug/spotless/generic/NativeCmdStep.java) | :white_large_square: | :+1: | :white_large_square: | :white_large_square: |
| [`generic.ReplaceRegexStep`](lib/src/main/java/com/diffplug/spotless/generic/ReplaceRegexStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`generic.ReplaceStep`](lib/src/main/java/com/diffplug/spotless/generic/ReplaceStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`generic.TrimTrailingWhitespaceStep`](lib/src/main/java/com/diffplug/spotless/generic/TrimTrailingWhitespaceStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
@@ -104,6 +105,7 @@ extra('wtp.EclipseWtpFormatterStep') +'{{yes}} | {{yes}}
| [`markdown.FreshMarkStep`](lib/src/main/java/com/diffplug/spotless/markdown/FreshMarkStep.java) | :+1: | :white_large_square: | :white_large_square: | :white_large_square: |
| [`npm.PrettierFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`npm.TsFmtFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
+| [`pom.SortPomStepStep`](lib/src/main/java/com/diffplug/spotless/pom/SortPomStepStep.java) | :white_large_square: | :+1: | :white_large_square: | :white_large_square: |
| [`python.BlackStep`](lib/src/main/java/com/diffplug/spotless/python/BlackStep.java) | :+1: | :white_large_square: | :white_large_square: | :white_large_square: |
| [`scala.ScalaFmtStep`](lib/src/main/java/com/diffplug/spotless/scala/ScalaFmtStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
| [`sql.DBeaverSQLFormatterStep`](lib/src/main/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
diff --git a/lib/build.gradle b/lib/build.gradle
index d8685a5acd..7120c21aea 100644
--- a/lib/build.gradle
+++ b/lib/build.gradle
@@ -6,15 +6,35 @@ version = rootProject.spotlessChangelog.versionNext
apply from: rootProject.file('gradle/java-setup.gradle')
apply from: rootProject.file('gradle/java-publish.gradle')
+def NEEDS_GLUE = [
+ 'sortPom'
+]
+for (glue in NEEDS_GLUE) {
+ sourceSets.register(glue) {
+ compileClasspath += sourceSets.main.output
+ runtimeClasspath += sourceSets.main.output
+ java {}
+ }
+}
+
dependencies {
// zero runtime reqs is a hard requirements for spotless-lib
// if you need a dep, put it in lib-extra
testImplementation "org.junit.jupiter:junit-jupiter:${VER_JUNIT}"
testImplementation "org.assertj:assertj-core:${VER_ASSERTJ}"
testImplementation "com.diffplug.durian:durian-testlib:${VER_DURIAN}"
+
+ // used for pom sorting
+ sortPomCompileOnly 'com.github.ekryd.sortpom:sortpom-sorter:3.0.0'
}
// we'll hold the core lib to a high standard
spotbugs { reportLevel = 'low' } // low|medium|high (low = sensitive to even minor mistakes)
test { useJUnitPlatform() }
+
+jar {
+ for (glue in NEEDS_GLUE) {
+ from sourceSets.getByName(glue).output.classesDirs
+ }
+}
diff --git a/lib/src/main/java/com/diffplug/spotless/FeatureClassLoader.java b/lib/src/main/java/com/diffplug/spotless/FeatureClassLoader.java
index c0d8163b05..506e29f49c 100644
--- a/lib/src/main/java/com/diffplug/spotless/FeatureClassLoader.java
+++ b/lib/src/main/java/com/diffplug/spotless/FeatureClassLoader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 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.
@@ -15,11 +15,13 @@
*/
package com.diffplug.spotless;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
+import java.nio.ByteBuffer;
+import java.security.ProtectionDomain;
import java.util.Objects;
import javax.annotation.Nullable;
@@ -29,37 +31,31 @@
* path of URLs.
* Features shall be independent from build tools. Hence the class loader of the
* underlying build tool is e.g. skipped during the the search for classes.
- * Only {@link #BUILD_TOOLS_PACKAGES } are explicitly looked up from the class loader of
- * the build tool and the provided URLs are ignored. This allows the feature to use
- * distinct functionality of the build tool.
+ *
+ * For `com.diffplug.spotless.glue.`, classes are redefined from within the lib jar
+ * but linked against the `Url[]`. This allows us to ship classfiles which function as glue
+ * code but delay linking/definition to runtime after the user has specified which version
+ * of the formatter they want.
+ *
+ * For `"org.slf4j.` and (`com.diffplug.spotless.` but not `com.diffplug.spotless.extra.`)
+ * the classes are loaded from the buildToolClassLoader.
*/
class FeatureClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
- /**
- * The following packages must be provided by the build tool or the corresponding Spotless plugin:
- *
- * - org.slf4j - SLF4J API must be provided. If no SLF4J binding is provided, log messages are dropped.
- *
- */
- static final List BUILD_TOOLS_PACKAGES = Collections.unmodifiableList(Arrays.asList("org.slf4j."));
- // NOTE: if this changes, you need to also update the `JarState.getClassLoader` methods.
-
private final ClassLoader buildToolClassLoader;
/**
* Constructs a new FeatureClassLoader for the given URLs, based on an {@code URLClassLoader},
- * using the system class loader as parent. For {@link #BUILD_TOOLS_PACKAGES }, the build
- * tool class loader is used.
+ * using the system class loader as parent.
*
* @param urls the URLs from which to load classes and resources
* @param buildToolClassLoader The build tool class loader
* @exception SecurityException If a security manager exists and prevents the creation of a class loader.
* @exception NullPointerException if {@code urls} is {@code null}.
*/
-
FeatureClassLoader(URL[] urls, ClassLoader buildToolClassLoader) {
super(urls, getParentClassLoader());
Objects.requireNonNull(buildToolClassLoader);
@@ -68,12 +64,53 @@ class FeatureClassLoader extends URLClassLoader {
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
- for (String buildToolPackage : BUILD_TOOLS_PACKAGES) {
- if (name.startsWith(buildToolPackage)) {
- return buildToolClassLoader.loadClass(name);
+ if (name.startsWith("com.diffplug.spotless.glue.")) {
+ String path = name.replace('.', '/') + ".class";
+ URL url = findResource(path);
+ if (url == null) {
+ throw new ClassNotFoundException(name);
+ }
+ try {
+ return defineClass(name, urlToByteBuffer(url), (ProtectionDomain) null);
+ } catch (IOException e) {
+ throw new ClassNotFoundException(name, e);
}
+ } else if (useBuildToolClassLoader(name)) {
+ return buildToolClassLoader.loadClass(name);
+ } else {
+ return super.findClass(name);
+ }
+ }
+
+ private static boolean useBuildToolClassLoader(String name) {
+ if (name.startsWith("org.slf4j.")) {
+ return true;
+ } else if (!name.startsWith("com.diffplug.spotless.extra") && name.startsWith("com.diffplug.spotless.")) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public URL findResource(String name) {
+ URL resource = super.findResource(name);
+ if (resource != null) {
+ return resource;
+ }
+ return buildToolClassLoader.getResource(name);
+ }
+
+ private static ByteBuffer urlToByteBuffer(URL url) throws IOException {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ int nRead;
+ byte[] data = new byte[1024];
+ InputStream inputStream = url.openStream();
+ while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
+ buffer.write(data, 0, nRead);
}
- return super.findClass(name);
+ buffer.flush();
+ return ByteBuffer.wrap(buffer.toByteArray());
}
/**
diff --git a/lib/src/main/java/com/diffplug/spotless/pom/SortPomCfg.java b/lib/src/main/java/com/diffplug/spotless/pom/SortPomCfg.java
new file mode 100644
index 0000000000..93c11e1e31
--- /dev/null
+++ b/lib/src/main/java/com/diffplug/spotless/pom/SortPomCfg.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 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.pom;
+
+import java.io.Serializable;
+
+// Class and members must be public, otherwise we get failed to access class com.diffplug.spotless.pom.SortPomInternalState from class com.diffplug.spotless.pom.SortPomFormatterFunc (com.diffplug.spotless.pom.SortPomInternalState is in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @682bd3c4; com.diffplug.spotless.pom.SortPomFormatterFunc is in unnamed module of loader com.diffplug.spotless.pom.DelegatingClassLoader @573284a5)
+public class SortPomCfg implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public String encoding = "UTF-8";
+
+ public String lineSeparator = System.getProperty("line.separator");
+
+ public boolean expandEmptyElements = true;
+
+ public boolean spaceBeforeCloseEmptyElement = false;
+
+ public boolean keepBlankLines = true;
+
+ public int nrOfIndentSpace = 2;
+
+ public boolean indentBlankLines = false;
+
+ public boolean indentSchemaLocation = false;
+
+ public String predefinedSortOrder = "recommended_2008_06";
+
+ public String sortOrderFile = null;
+
+ public String sortDependencies = null;
+
+ public String sortDependencyExclusions = null;
+
+ public String sortPlugins = null;
+
+ public boolean sortProperties = false;
+
+ public boolean sortModules = false;
+
+ public boolean sortExecutions = false;
+}
diff --git a/lib/src/main/java/com/diffplug/spotless/pom/SortPomStep.java b/lib/src/main/java/com/diffplug/spotless/pom/SortPomStep.java
new file mode 100644
index 0000000000..76965394a5
--- /dev/null
+++ b/lib/src/main/java/com/diffplug/spotless/pom/SortPomStep.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 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.pom;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import com.diffplug.spotless.FormatterFunc;
+import com.diffplug.spotless.FormatterStep;
+import com.diffplug.spotless.JarState;
+import com.diffplug.spotless.Provisioner;
+
+public class SortPomStep {
+ public static final String NAME = "sortPom";
+
+ private SortPomStep() {}
+
+ private SortPomCfg cfg;
+
+ public static FormatterStep create(SortPomCfg cfg, Provisioner provisioner) {
+ return FormatterStep.createLazy(NAME, () -> new State(cfg, provisioner), State::createFormat);
+ }
+
+ static class State implements Serializable {
+ SortPomCfg cfg;
+ JarState jarState;
+
+ public State(SortPomCfg cfg, Provisioner provisioner) throws IOException {
+ this.cfg = cfg;
+ this.jarState = JarState.from("com.github.ekryd.sortpom:sortpom-sorter:3.0.0", provisioner);
+ }
+
+ FormatterFunc createFormat() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
+ Class> formatterFunc = jarState.getClassLoader().loadClass("com.diffplug.spotless.glue.pom.SortPomFormatterFunc");
+ Constructor> constructor = formatterFunc.getConstructor(SortPomCfg.class);
+ return (FormatterFunc) constructor.newInstance(cfg);
+ }
+ }
+}
diff --git a/lib/src/sortPom/java/com/diffplug/spotless/glue/pom/SortPomFormatterFunc.java b/lib/src/sortPom/java/com/diffplug/spotless/glue/pom/SortPomFormatterFunc.java
new file mode 100644
index 0000000000..4a72c48fa9
--- /dev/null
+++ b/lib/src/sortPom/java/com/diffplug/spotless/glue/pom/SortPomFormatterFunc.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 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.glue.pom;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.logging.Logger;
+
+import org.apache.commons.io.IOUtils;
+
+import com.diffplug.spotless.FormatterFunc;
+import com.diffplug.spotless.pom.SortPomCfg;
+
+import sortpom.SortPomImpl;
+import sortpom.logger.SortPomLogger;
+import sortpom.parameter.PluginParameters;
+
+public class SortPomFormatterFunc implements FormatterFunc {
+ private static final Logger logger = Logger.getLogger(SortPomFormatterFunc.class.getName());
+ private final SortPomCfg cfg;
+
+ public SortPomFormatterFunc(SortPomCfg cfg) {
+ this.cfg = cfg;
+ }
+
+ @Override
+ public String apply(String input) throws Exception {
+ // SortPom expects a file to sort, so we write the inpout into a temporary file
+ File pom = File.createTempFile("pom", ".xml");
+ pom.deleteOnExit();
+ IOUtils.write(input, new FileOutputStream(pom), cfg.encoding);
+ SortPomImpl sortPom = new SortPomImpl();
+ sortPom.setup(new MySortPomLogger(), PluginParameters.builder()
+ .setPomFile(pom)
+ .setFileOutput(false, null, null, false)
+ .setEncoding(cfg.encoding)
+ .setFormatting(cfg.lineSeparator, cfg.expandEmptyElements, cfg.spaceBeforeCloseEmptyElement, cfg.keepBlankLines)
+ .setIndent(cfg.nrOfIndentSpace, cfg.indentBlankLines, cfg.indentSchemaLocation)
+ .setSortOrder(cfg.sortOrderFile, cfg.predefinedSortOrder)
+ .setSortEntities(cfg.sortDependencies, cfg.sortDependencyExclusions, cfg.sortPlugins, cfg.sortProperties, cfg.sortModules, cfg.sortExecutions)
+ .setTriggers(false)
+ .build());
+ sortPom.sortPom();
+ return IOUtils.toString(new FileInputStream(pom), cfg.encoding);
+ }
+
+ private static class MySortPomLogger implements SortPomLogger {
+ @Override
+ public void warn(String content) {
+ logger.warning(content);
+ }
+
+ @Override
+ public void info(String content) {
+ logger.info(content);
+ }
+
+ @Override
+ public void error(String content) {
+ logger.severe(content);
+ }
+ }
+}
diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md
index 7b9aa00a55..c3171ae050 100644
--- a/plugin-maven/CHANGES.md
+++ b/plugin-maven/CHANGES.md
@@ -5,6 +5,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
## [Unreleased]
### Added
* Added support for custom JSR223 formatters ([#945](https://github.com/diffplug/spotless/pull/945))
+* Added support for formating and sorting Maven POMs ([#946](https://github.com/diffplug/spotless/pull/946))
## [2.14.0] - 2021-09-27
### Added
diff --git a/plugin-maven/README.md b/plugin-maven/README.md
index 0136504299..cfff6897d3 100644
--- a/plugin-maven/README.md
+++ b/plugin-maven/README.md
@@ -55,6 +55,7 @@ user@machine repo % mvn spotless:check
- [Python](#python) ([black](#black))
- [Antlr4](#antlr4) ([antlr4formatter](#antlr4formatter))
- [Sql](#sql) ([dbeaver](#dbeaver))
+ - [Maven Pom](#maven-pom) ([sortPom](#sortpom))
- [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier))
- Multiple languages
- [Prettier](#prettier) ([plugins](#prettier-plugins), [npm detection](#npm-detection), [`.npmrc` detection](#npmrc-detection))
@@ -511,6 +512,67 @@ sql.formatter.indent.type=space
sql.formatter.indent.size=4
```
+## Maven POM
+
+[code](https://github.com/diffplug/spotless/blob/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/pom/Pom.java). [available steps](https://github.com/diffplug/spotless/tree/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/pom/SortPom.java).
+
+```xml
+
+
+
+
+ pom.xml
+
+
+
+
+
+
+```
+
+### sortPom
+
+[homepage](https://github.com/Ekryd/sortpom). [code](https://github.com/diffplug/spotless/tree/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/pom/SortPom.java).
+
+All configuration settings are optional, they are described in detail [here](https://github.com/Ekryd/sortpom/wiki/Parameters).
+
+```xml
+
+
+ UTF-8
+
+ ${line.separator}
+
+ true
+
+ false
+
+ true
+
+ 2
+
+ false
+
+ false
+
+ recommended_2008_06
+
+
+
+
+
+
+
+
+
+ false
+
+ false
+
+ false
+
+```
+
## Typescript
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 c39fc0a30c..245ab80a3a 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
@@ -56,6 +56,7 @@
import com.diffplug.spotless.maven.groovy.Groovy;
import com.diffplug.spotless.maven.java.Java;
import com.diffplug.spotless.maven.kotlin.Kotlin;
+import com.diffplug.spotless.maven.pom.Pom;
import com.diffplug.spotless.maven.python.Python;
import com.diffplug.spotless.maven.scala.Scala;
import com.diffplug.spotless.maven.sql.Sql;
@@ -120,6 +121,9 @@ public abstract class AbstractSpotlessMojo extends AbstractMojo {
@Parameter
private Antlr4 antlr4;
+ @Parameter
+ private Pom pom;
+
@Parameter
private Sql sql;
@@ -258,7 +262,7 @@ private FileLocator getFileLocator() {
}
private List getFormatterFactories() {
- return Stream.concat(formats.stream(), Stream.of(groovy, java, scala, kotlin, cpp, typescript, antlr4, sql, python))
+ return Stream.concat(formats.stream(), Stream.of(groovy, java, scala, kotlin, cpp, typescript, antlr4, pom, sql, python))
.filter(Objects::nonNull)
.collect(toList());
}
diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/pom/Pom.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/pom/Pom.java
new file mode 100644
index 0000000000..f083a17c89
--- /dev/null
+++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/pom/Pom.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 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.maven.pom;
+
+import java.util.Set;
+
+import com.diffplug.common.collect.ImmutableSet;
+import com.diffplug.spotless.maven.FormatterFactory;
+import com.diffplug.spotless.maven.generic.LicenseHeader;
+
+/**
+ * A {@link FormatterFactory} implementation that corresponds to {@code ...} configuration element.
+ *
+ * It defines a formatter for Maven pom files that can execute both language agnostic (e.g. {@link LicenseHeader})
+ * and pom-specific (e.g. {@link SortPom}) steps.
+ */
+public class Pom extends FormatterFactory {
+ @Override
+ public Set defaultIncludes() {
+ return ImmutableSet.of("pom.xml");
+ }
+
+ @Override
+ public String licenseHeaderDelimiter() {
+ return null;
+ }
+
+ public void addSortPom(SortPom sortPom) {
+ addStepFactory(sortPom);
+ }
+
+}
diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/pom/SortPom.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/pom/SortPom.java
new file mode 100644
index 0000000000..f4fe8cb96b
--- /dev/null
+++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/pom/SortPom.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 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.maven.pom;
+
+import org.apache.maven.plugins.annotations.Parameter;
+
+import com.diffplug.spotless.FormatterStep;
+import com.diffplug.spotless.maven.FormatterStepConfig;
+import com.diffplug.spotless.maven.FormatterStepFactory;
+import com.diffplug.spotless.pom.SortPomCfg;
+import com.diffplug.spotless.pom.SortPomStep;
+
+public class SortPom implements FormatterStepFactory {
+ private final SortPomCfg defaultValues = new SortPomCfg();
+
+ @Parameter
+ String encoding = defaultValues.encoding;
+
+ @Parameter
+ String lineSeparator = defaultValues.lineSeparator;
+
+ @Parameter
+ boolean expandEmptyElements = defaultValues.expandEmptyElements;
+
+ @Parameter
+ boolean spaceBeforeCloseEmptyElement = defaultValues.spaceBeforeCloseEmptyElement;
+
+ @Parameter
+ boolean keepBlankLines = defaultValues.keepBlankLines;
+
+ @Parameter
+ int nrOfIndentSpace = defaultValues.nrOfIndentSpace;
+
+ @Parameter
+ boolean indentBlankLines = defaultValues.indentBlankLines;
+
+ @Parameter
+ boolean indentSchemaLocation = defaultValues.indentSchemaLocation;
+
+ @Parameter
+ String predefinedSortOrder = defaultValues.predefinedSortOrder;
+
+ @Parameter
+ String sortOrderFile = defaultValues.sortOrderFile;
+
+ @Parameter
+ String sortDependencies = defaultValues.sortDependencies;
+
+ @Parameter
+ String sortDependencyExclusions = defaultValues.sortDependencyExclusions;
+
+ @Parameter
+ String sortPlugins = defaultValues.sortPlugins;
+
+ @Parameter
+ boolean sortProperties = defaultValues.sortProperties;
+
+ @Parameter
+ boolean sortModules = defaultValues.sortModules;
+
+ @Parameter
+ boolean sortExecutions = defaultValues.sortExecutions;
+
+ @Override
+ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) {
+ SortPomCfg cfg = new SortPomCfg();
+ cfg.encoding = encoding;
+ cfg.lineSeparator = lineSeparator;
+ cfg.expandEmptyElements = expandEmptyElements;
+ cfg.spaceBeforeCloseEmptyElement = spaceBeforeCloseEmptyElement;
+ cfg.keepBlankLines = keepBlankLines;
+ cfg.nrOfIndentSpace = nrOfIndentSpace;
+ cfg.indentBlankLines = indentBlankLines;
+ cfg.indentSchemaLocation = indentSchemaLocation;
+ cfg.predefinedSortOrder = predefinedSortOrder;
+ cfg.sortOrderFile = sortOrderFile;
+ cfg.sortDependencies = sortDependencies;
+ cfg.sortDependencyExclusions = sortDependencyExclusions;
+ cfg.sortPlugins = sortPlugins;
+ cfg.sortProperties = sortProperties;
+ cfg.sortModules = sortModules;
+ cfg.sortExecutions = sortExecutions;
+ return SortPomStep.create(cfg, stepConfig.getProvisioner());
+ }
+}
diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java
index a7dd49b2fb..fa5224bb8d 100644
--- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java
+++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java
@@ -134,6 +134,10 @@ protected void writePomWithPrettierSteps(String includes, String... steps) throw
writePom(formats(groupWithSteps("format", including(includes), steps)));
}
+ protected void writePomWithPomSteps(String... steps) throws IOException {
+ writePom(groupWithSteps("pom", including("pom_test.xml"), steps));
+ }
+
protected void writePom(String... configuration) throws IOException {
writePom(null, configuration);
}
diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/pom/SortPomMavenTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/pom/SortPomMavenTest.java
new file mode 100644
index 0000000000..ca9d77a6e3
--- /dev/null
+++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/pom/SortPomMavenTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 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.maven.pom;
+
+import org.junit.jupiter.api.Test;
+
+import com.diffplug.spotless.maven.MavenIntegrationHarness;
+
+public class SortPomMavenTest extends MavenIntegrationHarness {
+ @Test
+ public void testSortPomWithDefaultConfig() throws Exception {
+ writePomWithPomSteps("");
+
+ setFile("pom_test.xml").toResource("pom/pom_dirty.xml");
+ mavenRunner().withArguments("spotless:apply").runNoError().error();
+ assertFile("pom_test.xml").sameAsResource("pom/pom_clean_default.xml");
+ }
+}
diff --git a/testlib/build.gradle b/testlib/build.gradle
index 9975a9db26..edf9196d38 100644
--- a/testlib/build.gradle
+++ b/testlib/build.gradle
@@ -7,6 +7,7 @@ apply from: rootProject.file('gradle/java-setup.gradle')
dependencies {
api project(':lib')
+ api files(project(':lib').sourceSets.sortPom.output.classesDirs)
api "com.diffplug.durian:durian-core:${VER_DURIAN}"
api "com.diffplug.durian:durian-testlib:${VER_DURIAN}"
api "org.junit.jupiter:junit-jupiter:${VER_JUNIT}"
diff --git a/testlib/src/main/resources/pom/pom_clean_default.xml b/testlib/src/main/resources/pom/pom_clean_default.xml
new file mode 100644
index 0000000000..b4173f3b49
--- /dev/null
+++ b/testlib/src/main/resources/pom/pom_clean_default.xml
@@ -0,0 +1,40 @@
+
+
+ 4.0.0
+
+ spotless.test
+ minimal-pom
+ 1.0-SNAPSHOT
+ jar
+
+ minimal-pom
+ http://maven.apache.org
+
+
+ UTF-8
+ 1.8
+
+
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.1
+
+
+ ${java.version}
+
+
+
+
+
diff --git a/testlib/src/main/resources/pom/pom_dirty.xml b/testlib/src/main/resources/pom/pom_dirty.xml
new file mode 100644
index 0000000000..6d019c7df6
--- /dev/null
+++ b/testlib/src/main/resources/pom/pom_dirty.xml
@@ -0,0 +1,41 @@
+
+ minimal-pom
+ 4.0.0
+
+ spotless.test
+ jar
+ 1.0-SNAPSHOT
+
+ minimal-pom
+ http://maven.apache.org
+
+
+ UTF-8
+ 1.8
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.1
+
+
+ ${java.version}
+
+
+
+
+
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+
diff --git a/testlib/src/test/java/com/diffplug/spotless/pom/SortPomTest.java b/testlib/src/test/java/com/diffplug/spotless/pom/SortPomTest.java
new file mode 100644
index 0000000000..f397962086
--- /dev/null
+++ b/testlib/src/test/java/com/diffplug/spotless/pom/SortPomTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 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.pom;
+
+import org.junit.jupiter.api.Test;
+
+import com.diffplug.spotless.Provisioner;
+import com.diffplug.spotless.StepHarness;
+import com.diffplug.spotless.TestProvisioner;
+
+public class SortPomTest {
+ @Test
+ public void testSortPomWithDefaultConfig() throws Exception {
+ SortPomCfg cfg = new SortPomCfg();
+ Provisioner provisioner = TestProvisioner.mavenCentral();
+ StepHarness harness = StepHarness.forStep(SortPomStep.create(cfg, provisioner));
+ harness.testResource("pom/pom_dirty.xml", "pom/pom_clean_default.xml");
+ }
+}