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: - * - */ - 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} + ${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} + ${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"); + } +}