From cf434f2eecf9a9369f22c1c95f56adbb792df30f Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Mon, 30 Jan 2023 23:19:53 +0400 Subject: [PATCH 01/13] Start with Cleanthat Java refactorer --- .../java/JavaCleanthatRefactorerFunc.java | 56 +++++++ .../spotless/java/CleanthatStepFactory.java | 140 ++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java create mode 100644 lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java diff --git a/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java new file mode 100644 index 0000000000..bc96f0019b --- /dev/null +++ b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java @@ -0,0 +1,56 @@ +/* + * Copyright 2021-2023 Solven + * + * 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.java; + +import com.diffplug.spotless.FormatterFunc; +import eu.solven.cleanthat.config.pojo.CleanthatEngineProperties; +import eu.solven.cleanthat.engine.java.refactorer.JavaRefactorer; +import eu.solven.cleanthat.engine.java.refactorer.JavaRefactorerProperties; +import eu.solven.cleanthat.formatter.LineEnding; +import eu.solven.cleanthat.formatter.PathAndContent; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class JavaCleanthatRefactorerFunc implements FormatterFunc { + private List included; + private List excluded; + + public JavaCleanthatRefactorerFunc(List included, List excluded) { + this.included = included == null ? Collections.emptyList() : included; + this.excluded = excluded == null ? Collections.emptyList() : excluded; + } + + public JavaCleanthatRefactorerFunc() { + this(Arrays.asList(JavaRefactorerProperties.WILDCARD), Arrays.asList()); + } + + @Override + public String apply(String input) throws Exception { + JavaRefactorerProperties refactorerProperties = new JavaRefactorerProperties(); + + refactorerProperties.setIncluded(included); + refactorerProperties.setExcluded(excluded); + + JavaRefactorer refactorer = + new JavaRefactorer(CleanthatEngineProperties.builder().build(), refactorerProperties); + + // Spotless calls steps always with LF eol. + return refactorer.doFormat(new PathAndContent(Paths.get("fake"), input), LineEnding.LF); + } + +} diff --git a/lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java b/lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java new file mode 100644 index 0000000000..fa9936fb1e --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java @@ -0,0 +1,140 @@ +/* + * Copyright 2016-2023 Solven + * + * 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.java; + +import com.diffplug.spotless.FormatterFunc; +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.JarState; +import com.diffplug.spotless.Jvm; +import com.diffplug.spotless.Provisioner; +import java.io.IOException; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Objects; + +/** + * Enables CleanThat as a SpotLess step. This may be moved to Spotless own repo + * (https://github.com/diffplug/spotless/tree/main/lib) + * + * @author Benoit Lacelle + */ +// https://github.com/diffplug/spotless/blob/main/CONTRIBUTING.md#how-to-add-a-new-formatterstep +public final class CleanthatStepFactory { + + private static final String NAME = "cleanthat"; + private static final String MAVEN_COORDINATE = "io.github.solven-eu.cleanthat:java:"; + + private static final Jvm.Support JVM_SUPPORT = Jvm.support(NAME).add(8, "2.0"); + + // prevent direct instantiation + private CleanthatStepFactory() { + } + + /** Creates a step which formats everything - code, import order, and unused imports. */ + public static FormatterStep create(Provisioner provisioner) { + return create(defaultVersion(), provisioner); + } + + /** Creates a step which formats everything - code, import order, and unused imports. */ + public static FormatterStep create(String version, Provisioner provisioner) { + return create(MAVEN_COORDINATE, version, defaultExcluded(), defaultIncluded(), provisioner); + } + + private static List defaultExcluded() { + return List.of(); + } + + private static List defaultIncluded() { + return List.of("*"); + } + + /** Creates a step which formats everything - groupArtifact, code, import order, and unused imports. */ + public static FormatterStep create(String groupArtifact, + String version, + List excluded, + List included, + Provisioner provisioner) { + Objects.requireNonNull(groupArtifact, "groupArtifact"); + if (groupArtifact.chars().filter(ch -> ch == ':').count() != 1) { + throw new IllegalArgumentException("groupArtifact must be in the form 'groupId:artifactId'"); + } + Objects.requireNonNull(version, "version"); + Objects.requireNonNull(provisioner, "provisioner"); + return FormatterStep.createLazy(NAME, + () -> new JavaRulesState(NAME, groupArtifact, version, excluded, included, provisioner), + JavaRulesState::createFormat); + } + + /** Get default formatter version */ + public static String defaultVersion() { + return JVM_SUPPORT.getRecommendedFormatterVersion(); + } + + static final class JavaRulesState implements Serializable { + private static final long serialVersionUID = 1L; + + final JarState jarState; + final String stepName; + final String version; + + final List included; + final List excluded; + + JavaRulesState(String stepName, String version, Provisioner provisioner) throws IOException { + this(stepName, MAVEN_COORDINATE, version, defaultExcluded(), defaultIncluded(), provisioner); + } + + JavaRulesState(String stepName, + String groupArtifact, + String version, + List included, + List excluded, + Provisioner provisioner) throws IOException { + JVM_SUPPORT.assertFormatterSupported(version); + // ModuleHelper.doOpenInternalPackagesIfRequired(); + this.jarState = JarState.from(groupArtifact + ":" + version, provisioner); + this.stepName = stepName; + this.version = version; + + this.included = included; + this.excluded = excluded; + } + + @SuppressWarnings("PMD.UseProperClassLoader") + FormatterFunc createFormat() { + ClassLoader classLoader = jarState.getClassLoader(); + + Object formatter; + Method formatterMethod; + try { + Class formatterClazz = + classLoader.loadClass("com.diffplug.spotless.glue.java.JavaCleanthatRefactoringFunc"); + Constructor formatterConstructor = formatterClazz.getConstructor(List.class, List.class); + + formatter = formatterConstructor.newInstance(included, excluded); + formatterMethod = formatterClazz.getMethod("apply", String.class); + } catch (ReflectiveOperationException e) { + throw new IllegalStateException("Issue executing the formatter", e); + } + return JVM_SUPPORT.suggestLaterVersionOnError(version, input -> { + return (String) formatterMethod.invoke(formatter, input); + }); + } + + } +} From 7ea0f3ab07acee00c4ece3bb2f8af0c9810dcc23 Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Mon, 6 Feb 2023 10:00:28 +0400 Subject: [PATCH 02/13] Add custom JDK version --- lib/build.gradle | 5 +- .../java/JavaCleanthatRefactorerFunc.java | 12 +++-- .../spotless/java/CleanthatStepFactory.java | 4 ++ .../spotless/maven/java/CleanthatJava.java | 53 +++++++++++++++++++ 4 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java diff --git a/lib/build.gradle b/lib/build.gradle index 9d95007bc1..9918e4c330 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -16,7 +16,8 @@ def NEEDS_GLUE = [ 'diktat', 'scalafmt', 'jackson', - 'gson' + 'gson', + 'cleanthat' ] for (glue in NEEDS_GLUE) { sourceSets.register(glue) { @@ -100,6 +101,8 @@ dependencies { flexmarkCompileOnly 'com.vladsch.flexmark:flexmark-all:0.62.2' gsonCompileOnly 'com.google.code.gson:gson:2.10.1' + + cleanthatCompileOnly 'io.github.solven-eu.cleanthat:java:2.0-SNAPSHOT' } // we'll hold the core lib to a high standard diff --git a/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java index bc96f0019b..e114234b3f 100644 --- a/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java +++ b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 Solven + * Copyright 2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,10 +27,12 @@ import java.util.List; public class JavaCleanthatRefactorerFunc implements FormatterFunc { + private String jdkVersion; private List included; private List excluded; - public JavaCleanthatRefactorerFunc(List included, List excluded) { + public JavaCleanthatRefactorerFunc(String jdkVersion, List included, List excluded) { + this.jdkVersion=jdkVersion == null ? IJdkVersionsConstant.JDK8 : jdkVersion; this.included = included == null ? Collections.emptyList() : included; this.excluded = excluded == null ? Collections.emptyList() : excluded; } @@ -41,16 +43,18 @@ public JavaCleanthatRefactorerFunc() { @Override public String apply(String input) throws Exception { + CleanthatEngineProperties engineProperties = CleanthatEngineProperties.builder().engineVersion(jdkVersion).build(); + JavaRefactorerProperties refactorerProperties = new JavaRefactorerProperties(); refactorerProperties.setIncluded(included); refactorerProperties.setExcluded(excluded); JavaRefactorer refactorer = - new JavaRefactorer(CleanthatEngineProperties.builder().build(), refactorerProperties); + new JavaRefactorer(engineProperties, refactorerProperties); // Spotless calls steps always with LF eol. - return refactorer.doFormat(new PathAndContent(Paths.get("fake"), input), LineEnding.LF); + return refactorer.doFormat(input, LineEnding.LF); } } diff --git a/lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java b/lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java index fa9936fb1e..2167052ff4 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java +++ b/lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java @@ -85,6 +85,10 @@ public static String defaultVersion() { return JVM_SUPPORT.getRecommendedFormatterVersion(); } + public static String defaultGroupArtifact() { + return MAVEN_COORDINATE; + } + static final class JavaRulesState implements Serializable { private static final long serialVersionUID = 1L; diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java new file mode 100644 index 0000000000..661dd7874f --- /dev/null +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java @@ -0,0 +1,53 @@ +/* + * 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. + * 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.java; + +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.java.CleanthatStepFactory; +import com.diffplug.spotless.java.GoogleJavaFormatStep; +import com.diffplug.spotless.maven.FormatterStepConfig; +import com.diffplug.spotless.maven.FormatterStepFactory; + +import org.apache.maven.plugins.annotations.Parameter; + +import java.util.List; + +public class CleanthatJava implements FormatterStepFactory { + @Parameter + private String groupArtifact; + + @Parameter + private String version; + + @Parameter + private List mutators; + + @Parameter + private List excludedMutators; + + @Override + public FormatterStep newFormatterStep(FormatterStepConfig config) { + String groupArtifact = this.groupArtifact != null ? this.groupArtifact : CleanthatStepFactory.defaultGroupArtifact(); + String version = this.version != null ? this.version : CleanthatStepFactory.defaultVersion(); + + JavaRe + boolean reflowLongStrings = this.reflowLongStrings != null ? this.reflowLongStrings : GoogleJavaFormatStep.defaultReflowLongStrings(); + return CleanthatStepFactory.create(groupArtifact, version, style, config.getProvisioner(), reflowLongStrings); + } + + private static String defaultVersion() { + } +} From 23c97a4042e8c83bb9f703e4bf95469c5308ef4e Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Mon, 6 Feb 2023 23:28:25 +0400 Subject: [PATCH 03/13] Clean implementation based on CleanThat 2.0 --- CHANGES.md | 2 + lib/build.gradle | 2 +- .../java/JavaCleanthatRefactorerFunc.java | 18 ++--- .../spotless/java/CleanthatStepFactory.java | 70 +++++++++++-------- plugin-maven/CHANGES.md | 2 + .../spotless/maven/java/CleanthatJava.java | 26 ++++--- 6 files changed, 68 insertions(+), 52 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5f1a1ffa9f..46b8b926d1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,8 @@ This document is intended for Spotless developers. We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Added +* CleanThat Java Refactorer ([#???](https://github.com/diffplug/spotless/pull/???)) ## [2.34.1] - 2023-02-05 ### Changes diff --git a/lib/build.gradle b/lib/build.gradle index 9918e4c330..0e1c2086ea 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -102,7 +102,7 @@ dependencies { gsonCompileOnly 'com.google.code.gson:gson:2.10.1' - cleanthatCompileOnly 'io.github.solven-eu.cleanthat:java:2.0-SNAPSHOT' + cleanthatCompileOnly 'io.github.solven-eu.cleanthat:java:2.0' } // we'll hold the core lib to a high standard diff --git a/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java index e114234b3f..f86c53daa3 100644 --- a/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java +++ b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java @@ -15,16 +15,17 @@ */ package com.diffplug.spotless.glue.java; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + import com.diffplug.spotless.FormatterFunc; + import eu.solven.cleanthat.config.pojo.CleanthatEngineProperties; +import eu.solven.cleanthat.engine.java.IJdkVersionConstants; import eu.solven.cleanthat.engine.java.refactorer.JavaRefactorer; import eu.solven.cleanthat.engine.java.refactorer.JavaRefactorerProperties; import eu.solven.cleanthat.formatter.LineEnding; -import eu.solven.cleanthat.formatter.PathAndContent; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; public class JavaCleanthatRefactorerFunc implements FormatterFunc { private String jdkVersion; @@ -32,13 +33,13 @@ public class JavaCleanthatRefactorerFunc implements FormatterFunc { private List excluded; public JavaCleanthatRefactorerFunc(String jdkVersion, List included, List excluded) { - this.jdkVersion=jdkVersion == null ? IJdkVersionsConstant.JDK8 : jdkVersion; + this.jdkVersion = jdkVersion == null ? IJdkVersionConstants.JDK_8 : jdkVersion; this.included = included == null ? Collections.emptyList() : included; this.excluded = excluded == null ? Collections.emptyList() : excluded; } public JavaCleanthatRefactorerFunc() { - this(Arrays.asList(JavaRefactorerProperties.WILDCARD), Arrays.asList()); + this(IJdkVersionConstants.JDK_8, Arrays.asList(JavaRefactorerProperties.WILDCARD), Arrays.asList()); } @Override @@ -50,8 +51,7 @@ public String apply(String input) throws Exception { refactorerProperties.setIncluded(included); refactorerProperties.setExcluded(excluded); - JavaRefactorer refactorer = - new JavaRefactorer(engineProperties, refactorerProperties); + JavaRefactorer refactorer = new JavaRefactorer(engineProperties, refactorerProperties); // Spotless calls steps always with LF eol. return refactorer.doFormat(input, LineEnding.LF); diff --git a/lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java b/lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java index 2167052ff4..b053f9f2e6 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java +++ b/lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 Solven + * Copyright 2023 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,6 @@ */ package com.diffplug.spotless.java; -import com.diffplug.spotless.FormatterFunc; -import com.diffplug.spotless.FormatterStep; -import com.diffplug.spotless.JarState; -import com.diffplug.spotless.Jvm; -import com.diffplug.spotless.Provisioner; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Constructor; @@ -27,9 +22,14 @@ import java.util.List; import java.util.Objects; +import com.diffplug.spotless.FormatterFunc; +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.JarState; +import com.diffplug.spotless.Jvm; +import com.diffplug.spotless.Provisioner; + /** - * Enables CleanThat as a SpotLess step. This may be moved to Spotless own repo - * (https://github.com/diffplug/spotless/tree/main/lib) + * Enables CleanThat as a SpotLess step. * * @author Benoit Lacelle */ @@ -39,33 +39,45 @@ public final class CleanthatStepFactory { private static final String NAME = "cleanthat"; private static final String MAVEN_COORDINATE = "io.github.solven-eu.cleanthat:java:"; - private static final Jvm.Support JVM_SUPPORT = Jvm.support(NAME).add(8, "2.0"); + private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(11, "2.0"); // prevent direct instantiation - private CleanthatStepFactory() { - } + private CleanthatStepFactory() {} - /** Creates a step which formats everything - code, import order, and unused imports. */ + /** Creates a step which apply default CleanThat mutators. */ public static FormatterStep create(Provisioner provisioner) { return create(defaultVersion(), provisioner); } - /** Creates a step which formats everything - code, import order, and unused imports. */ + /** Creates a step which apply default CleanThat mutators. */ public static FormatterStep create(String version, Provisioner provisioner) { - return create(MAVEN_COORDINATE, version, defaultExcluded(), defaultIncluded(), provisioner); + return create(MAVEN_COORDINATE, version, defaultJdkVersion(), defaultExcludedMutators(), defaultMutators(), provisioner); + } + + public static String defaultJdkVersion() { + // see IJdkVersionConstants.JDK_7 + // https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#source + // 1.7 is the default for 'maven-compiler-plugin' since 3.9.0 + return "1.7"; } - private static List defaultExcluded() { + public static List defaultExcludedMutators() { return List.of(); } - private static List defaultIncluded() { + /** + * By default, we include all available rules + * @return + */ + public static List defaultMutators() { + // see JavaRefactorerProperties.WILDCARD return List.of("*"); } - /** Creates a step which formats everything - groupArtifact, code, import order, and unused imports. */ + /** Creates a step which apply selected CleanThat mutators. */ public static FormatterStep create(String groupArtifact, String version, + String sourceJdkVersion, List excluded, List included, Provisioner provisioner) { @@ -76,8 +88,8 @@ public static FormatterStep create(String groupArtifact, Objects.requireNonNull(version, "version"); Objects.requireNonNull(provisioner, "provisioner"); return FormatterStep.createLazy(NAME, - () -> new JavaRulesState(NAME, groupArtifact, version, excluded, included, provisioner), - JavaRulesState::createFormat); + () -> new JavaRefactorerState(NAME, groupArtifact, version, sourceJdkVersion, excluded, included, provisioner), + JavaRefactorerState::createFormat); } /** Get default formatter version */ @@ -89,32 +101,35 @@ public static String defaultGroupArtifact() { return MAVEN_COORDINATE; } - static final class JavaRulesState implements Serializable { + static final class JavaRefactorerState implements Serializable { private static final long serialVersionUID = 1L; final JarState jarState; final String stepName; final String version; + final String sourceJdkVersion; final List included; final List excluded; - JavaRulesState(String stepName, String version, Provisioner provisioner) throws IOException { - this(stepName, MAVEN_COORDINATE, version, defaultExcluded(), defaultIncluded(), provisioner); + JavaRefactorerState(String stepName, String version, Provisioner provisioner) throws IOException { + this(stepName, MAVEN_COORDINATE, version, defaultJdkVersion(), defaultExcludedMutators(), defaultMutators(), provisioner); } - JavaRulesState(String stepName, + JavaRefactorerState(String stepName, String groupArtifact, String version, + String sourceJdkVersion, List included, List excluded, Provisioner provisioner) throws IOException { JVM_SUPPORT.assertFormatterSupported(version); - // ModuleHelper.doOpenInternalPackagesIfRequired(); + ModuleHelper.doOpenInternalPackagesIfRequired(); this.jarState = JarState.from(groupArtifact + ":" + version, provisioner); this.stepName = stepName; this.version = version; + this.sourceJdkVersion = sourceJdkVersion; this.included = included; this.excluded = excluded; } @@ -126,11 +141,10 @@ FormatterFunc createFormat() { Object formatter; Method formatterMethod; try { - Class formatterClazz = - classLoader.loadClass("com.diffplug.spotless.glue.java.JavaCleanthatRefactoringFunc"); - Constructor formatterConstructor = formatterClazz.getConstructor(List.class, List.class); + Class formatterClazz = classLoader.loadClass("com.diffplug.spotless.glue.java.JavaCleanthatRefactorerFunc"); + Constructor formatterConstructor = formatterClazz.getConstructor(String.class, List.class, List.class); - formatter = formatterConstructor.newInstance(included, excluded); + formatter = formatterConstructor.newInstance(sourceJdkVersion, included, excluded); formatterMethod = formatterClazz.getMethod("apply", String.class); } catch (ReflectiveOperationException e) { throw new IllegalStateException("Issue executing the formatter", e); diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 567d880f4f..fc276f7c8d 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Added +* CleanThat Java Refactorer ([#???](https://github.com/diffplug/spotless/pull/???)) ## [2.32.0] - 2023-02-05 ### Added diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java index 661dd7874f..76c130bcdd 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 DiffPlug + * Copyright 2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,16 +15,15 @@ */ package com.diffplug.spotless.maven.java; +import java.util.List; + +import org.apache.maven.plugins.annotations.Parameter; + import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.java.CleanthatStepFactory; -import com.diffplug.spotless.java.GoogleJavaFormatStep; import com.diffplug.spotless.maven.FormatterStepConfig; import com.diffplug.spotless.maven.FormatterStepFactory; -import org.apache.maven.plugins.annotations.Parameter; - -import java.util.List; - public class CleanthatJava implements FormatterStepFactory { @Parameter private String groupArtifact; @@ -32,22 +31,21 @@ public class CleanthatJava implements FormatterStepFactory { @Parameter private String version; + // https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#source + @Parameter(property = "maven.compiler.source") + private String sourceJdk = CleanthatStepFactory.defaultJdkVersion(); + @Parameter - private List mutators; + private List mutators = CleanthatStepFactory.defaultMutators(); @Parameter - private List excludedMutators; + private List excludedMutators = CleanthatStepFactory.defaultExcludedMutators(); @Override public FormatterStep newFormatterStep(FormatterStepConfig config) { String groupArtifact = this.groupArtifact != null ? this.groupArtifact : CleanthatStepFactory.defaultGroupArtifact(); String version = this.version != null ? this.version : CleanthatStepFactory.defaultVersion(); - JavaRe - boolean reflowLongStrings = this.reflowLongStrings != null ? this.reflowLongStrings : GoogleJavaFormatStep.defaultReflowLongStrings(); - return CleanthatStepFactory.create(groupArtifact, version, style, config.getProvisioner(), reflowLongStrings); - } - - private static String defaultVersion() { + return CleanthatStepFactory.create(groupArtifact, version, sourceJdk, mutators, excludedMutators, config.getProvisioner()); } } From e9efb1377f369e95c21505bd9b0a4aca080f0afb Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Tue, 7 Feb 2023 00:26:29 +0400 Subject: [PATCH 04/13] Add documentation, tests, and hit a wall as mutators are not detected --- README.md | 2 + .../java/JavaCleanthatRefactorerFunc.java | 8 ++ ...tepFactory.java => CleanthatJavaStep.java} | 8 +- plugin-maven/README.md | 23 +++++- .../spotless/maven/java/CleanthatJava.java | 14 ++-- .../diffplug/spotless/maven/java/Java.java | 4 + .../java/CleanthatJavaRefactorerTest.java | 76 +++++++++++++++++++ .../LiteralsFirstInComparisons.clean.java | 8 ++ .../LiteralsFirstInComparisons.dirty.java | 8 ++ .../cleanthat/MultipleMutators.clean.java | 14 ++++ ...tipleMutators.clean.onlyLiteralsFirst.java | 14 ++++ .../cleanthat/MultipleMutators.dirty.java | 14 ++++ 12 files changed, 181 insertions(+), 12 deletions(-) rename lib/src/main/java/com/diffplug/spotless/java/{CleanthatStepFactory.java => CleanthatJavaStep.java} (97%) create mode 100644 plugin-maven/src/test/java/com/diffplug/spotless/maven/java/CleanthatJavaRefactorerTest.java create mode 100644 testlib/src/main/resources/java/cleanthat/LiteralsFirstInComparisons.clean.java create mode 100644 testlib/src/main/resources/java/cleanthat/LiteralsFirstInComparisons.dirty.java create mode 100644 testlib/src/main/resources/java/cleanthat/MultipleMutators.clean.java create mode 100644 testlib/src/main/resources/java/cleanthat/MultipleMutators.clean.onlyLiteralsFirst.java create mode 100644 testlib/src/main/resources/java/cleanthat/MultipleMutators.dirty.java diff --git a/README.md b/README.md index d2527034ca..5f59368677 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ lib('java.PalantirJavaFormatStep') +'{{yes}} | {{yes}} lib('java.RemoveUnusedImportsStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', extra('java.EclipseJdtFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', lib('java.FormatAnnotationsStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', +lib('java.CleanthatJavaStep') +'{{no}} | {{yes}} | {{no}} | {{no}} |', lib('json.gson.GsonStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('json.JacksonJsonStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('json.JsonSimpleStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', @@ -131,6 +132,7 @@ lib('yaml.JacksonYamlStep') +'{{yes}} | {{yes}} | [`java.RemoveUnusedImportsStep`](lib/src/main/java/com/diffplug/spotless/java/RemoveUnusedImportsStep.java) | :+1: | :+1: | :+1: | :white_large_square: | | [`java.EclipseJdtFormatterStep`](lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java) | :+1: | :+1: | :+1: | :white_large_square: | | [`java.FormatAnnotationsStep`](lib/src/main/java/com/diffplug/spotless/java/FormatAnnotationsStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | +| [`java.CleanthatJavaStep`](lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java) | :white_large_square: | :+1: | :white_large_square: | :white_large_square: | | [`json.gson.GsonStep`](lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | | [`json.JacksonJsonStep`](lib/src/main/java/com/diffplug/spotless/json/JacksonJsonStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | | [`json.JsonSimpleStep`](lib/src/main/java/com/diffplug/spotless/json/JsonSimpleStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | diff --git a/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java index f86c53daa3..5ec54e69be 100644 --- a/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java +++ b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java @@ -19,6 +19,9 @@ import java.util.Collections; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.diffplug.spotless.FormatterFunc; import eu.solven.cleanthat.config.pojo.CleanthatEngineProperties; @@ -28,6 +31,8 @@ import eu.solven.cleanthat.formatter.LineEnding; public class JavaCleanthatRefactorerFunc implements FormatterFunc { + private static final Logger LOGGER = LoggerFactory.getLogger(JavaCleanthatRefactorerFunc.class); + private String jdkVersion; private List included; private List excluded; @@ -53,6 +58,9 @@ public String apply(String input) throws Exception { JavaRefactorer refactorer = new JavaRefactorer(engineProperties, refactorerProperties); + LOGGER.debug("Processing sourceJdk={} included={} excluded={}", jdkVersion, included, excluded); + LOGGER.debug("Available mutators: {}", JavaRefactorer.getAllIncluded()); + // Spotless calls steps always with LF eol. return refactorer.doFormat(input, LineEnding.LF); } diff --git a/lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java b/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java similarity index 97% rename from lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java rename to lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java index b053f9f2e6..79434cba56 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/CleanthatStepFactory.java +++ b/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java @@ -34,15 +34,15 @@ * @author Benoit Lacelle */ // https://github.com/diffplug/spotless/blob/main/CONTRIBUTING.md#how-to-add-a-new-formatterstep -public final class CleanthatStepFactory { +public final class CleanthatJavaStep { private static final String NAME = "cleanthat"; - private static final String MAVEN_COORDINATE = "io.github.solven-eu.cleanthat:java:"; + private static final String MAVEN_COORDINATE = "io.github.solven-eu.cleanthat:java"; private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(11, "2.0"); // prevent direct instantiation - private CleanthatStepFactory() {} + private CleanthatJavaStep() {} /** Creates a step which apply default CleanThat mutators. */ public static FormatterStep create(Provisioner provisioner) { @@ -83,7 +83,7 @@ public static FormatterStep create(String groupArtifact, Provisioner provisioner) { Objects.requireNonNull(groupArtifact, "groupArtifact"); if (groupArtifact.chars().filter(ch -> ch == ':').count() != 1) { - throw new IllegalArgumentException("groupArtifact must be in the form 'groupId:artifactId'"); + throw new IllegalArgumentException("groupArtifact must be in the form 'groupId:artifactId'. it was: " + groupArtifact); } Objects.requireNonNull(version, "version"); Objects.requireNonNull(provisioner, "provisioner"); diff --git a/plugin-maven/README.md b/plugin-maven/README.md index 0787150ee1..36d46b2386 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -39,7 +39,7 @@ user@machine repo % mvn spotless:check - [Requirements](#requirements) - [Binding to maven phase](#binding-to-maven-phase) - **Languages** - - [Java](#java) ([google-java-format](#google-java-format), [eclipse jdt](#eclipse-jdt), [prettier](#prettier), [palantir-java-format](#palantir-java-format), [formatAnnotations](#formatAnnotations)) + - [Java](#java) ([google-java-format](#google-java-format), [eclipse jdt](#eclipse-jdt), [prettier](#prettier), [palantir-java-format](#palantir-java-format), [formatAnnotations](#formatAnnotations), [cleanthat](#cleanthat)) - [Groovy](#groovy) ([eclipse groovy](#eclipse-groovy)) - [Kotlin](#kotlin) ([ktfmt](#ktfmt), [ktlint](#ktlint), [diktat](#diktat), [prettier](#prettier)) - [Scala](#scala) ([scalafmt](#scalafmt)) @@ -182,6 +182,8 @@ any other maven phase (i.e. compile) then it can be configured as below; src/test/java/**/*.java + + @@ -274,6 +276,25 @@ list of well-known type annotations. You can make a pull request to add new one In the future there will be mechanisms to add/remove annotations from the list. These mechanisms already exist for the Gradle plugin. +### Cleanthat + +[homepage](https://github.com/solven-eu/cleanthat). CleanThat enables automatic refactoring of Java code + +```xml + + 2.0 + ${maven.compiler.source} + + * + + + LiteralsFirstInComparisons + + + OptionalNotEmpty + + +``` ## Groovy diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java index 76c130bcdd..7460ab67b9 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java @@ -20,7 +20,7 @@ import org.apache.maven.plugins.annotations.Parameter; import com.diffplug.spotless.FormatterStep; -import com.diffplug.spotless.java.CleanthatStepFactory; +import com.diffplug.spotless.java.CleanthatJavaStep; import com.diffplug.spotless.maven.FormatterStepConfig; import com.diffplug.spotless.maven.FormatterStepFactory; @@ -33,19 +33,19 @@ public class CleanthatJava implements FormatterStepFactory { // https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#source @Parameter(property = "maven.compiler.source") - private String sourceJdk = CleanthatStepFactory.defaultJdkVersion(); + private String sourceJdk = CleanthatJavaStep.defaultJdkVersion(); @Parameter - private List mutators = CleanthatStepFactory.defaultMutators(); + private List mutators = CleanthatJavaStep.defaultMutators(); @Parameter - private List excludedMutators = CleanthatStepFactory.defaultExcludedMutators(); + private List excludedMutators = CleanthatJavaStep.defaultExcludedMutators(); @Override public FormatterStep newFormatterStep(FormatterStepConfig config) { - String groupArtifact = this.groupArtifact != null ? this.groupArtifact : CleanthatStepFactory.defaultGroupArtifact(); - String version = this.version != null ? this.version : CleanthatStepFactory.defaultVersion(); + String groupArtifact = this.groupArtifact != null ? this.groupArtifact : CleanthatJavaStep.defaultGroupArtifact(); + String version = this.version != null ? this.version : CleanthatJavaStep.defaultVersion(); - return CleanthatStepFactory.create(groupArtifact, version, sourceJdk, mutators, excludedMutators, config.getProvisioner()); + return CleanthatJavaStep.create(groupArtifact, version, sourceJdk, mutators, excludedMutators, config.getProvisioner()); } } diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/Java.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/Java.java index 0921a838b3..efdf0827db 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/Java.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/Java.java @@ -79,6 +79,10 @@ public void addFormatAnnotations(FormatAnnotations formatAnnotations) { addStepFactory(formatAnnotations); } + public void addCleanthat(CleanthatJava cleanthat) { + addStepFactory(cleanthat); + } + private static String fileMask(Path path) { String dir = path.toString(); if (!dir.endsWith(File.separator)) { diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/CleanthatJavaRefactorerTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/CleanthatJavaRefactorerTest.java new file mode 100644 index 0000000000..c72aeb15b2 --- /dev/null +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/CleanthatJavaRefactorerTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2022-2023 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.java; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.diffplug.spotless.maven.MavenIntegrationHarness; + +class CleanthatJavaRefactorerTest extends MavenIntegrationHarness { + private static final Logger LOGGER = LoggerFactory.getLogger(CleanthatJavaRefactorerTest.class); + + @Test + void testLiteralsFirstInComparisons() throws Exception { + writePomWithJavaSteps( + "", + ""); + + runTest("LiteralsFirstInComparisons.dirty.java", "LiteralsFirstInComparisons.clean.java"); + } + + @Test + void testMultipleMutators() throws Exception { + writePomWithJavaSteps( + "", + ""); + + runTest("MultipleMutators.dirty.java", "MultipleMutators.clean.java"); + } + + @Test + void testExcludeOptionalNotEmpty() throws Exception { + writePomWithJavaSteps( + "", + " ", + " OptionalNotEmpty", + " ", + ""); + + runTest("MultipleMutators.dirty.java", "MultipleMutators.clean.onlyLiteralsFirst.java"); + } + + @Test + void testIncludeOnlyLiteralsFirstInComparisons() throws Exception { + writePomWithJavaSteps( + "", + " ", + " LiteralsFirstInComparisons", + " ", + ""); + + runTest("MultipleMutators.dirty.java", "MultipleMutators.clean.onlyLiteralsFirst.java"); + } + + private void runTest(String dirtyPath, String cleanPath) throws Exception { + String path = "src/main/java/test.java"; + setFile(path).toResource("java/cleanthat/" + dirtyPath); + Assertions.assertThat(mavenRunner().withArguments("spotless:apply -X").runNoError().stdOutUtf8()).isEmpty(); + assertFile(path).sameAsResource("java/cleanthat/" + cleanPath); + } +} diff --git a/testlib/src/main/resources/java/cleanthat/LiteralsFirstInComparisons.clean.java b/testlib/src/main/resources/java/cleanthat/LiteralsFirstInComparisons.clean.java new file mode 100644 index 0000000000..8bacfa58db --- /dev/null +++ b/testlib/src/main/resources/java/cleanthat/LiteralsFirstInComparisons.clean.java @@ -0,0 +1,8 @@ +package eu.solven.cleanthat.engine.java.refactorer.cases.do_not_format_me; + +public class LiteralsFirstInComparisonsCases { + + public boolean isHardcoded(String input) { + return "hardcoded".equals(input); + } +} diff --git a/testlib/src/main/resources/java/cleanthat/LiteralsFirstInComparisons.dirty.java b/testlib/src/main/resources/java/cleanthat/LiteralsFirstInComparisons.dirty.java new file mode 100644 index 0000000000..3a1e074c91 --- /dev/null +++ b/testlib/src/main/resources/java/cleanthat/LiteralsFirstInComparisons.dirty.java @@ -0,0 +1,8 @@ +package eu.solven.cleanthat.engine.java.refactorer.cases.do_not_format_me; + +public class LiteralsFirstInComparisonsCases { + + public boolean isHardcoded(String input) { + return input.equals("hardcoded"); + } +} diff --git a/testlib/src/main/resources/java/cleanthat/MultipleMutators.clean.java b/testlib/src/main/resources/java/cleanthat/MultipleMutators.clean.java new file mode 100644 index 0000000000..0829602dc1 --- /dev/null +++ b/testlib/src/main/resources/java/cleanthat/MultipleMutators.clean.java @@ -0,0 +1,14 @@ +package eu.solven.cleanthat.engine.java.refactorer.cases.do_not_format_me; + +import java.util.Optional; + +public class LiteralsFirstInComparisonsCases { + + public boolean isHardcoded(String input) { + return input.equals("hardcoded"); + } + + public boolean isPresent(Optional optional) { + return optional.isPresent(); + } +} diff --git a/testlib/src/main/resources/java/cleanthat/MultipleMutators.clean.onlyLiteralsFirst.java b/testlib/src/main/resources/java/cleanthat/MultipleMutators.clean.onlyLiteralsFirst.java new file mode 100644 index 0000000000..629d24504b --- /dev/null +++ b/testlib/src/main/resources/java/cleanthat/MultipleMutators.clean.onlyLiteralsFirst.java @@ -0,0 +1,14 @@ +package eu.solven.cleanthat.engine.java.refactorer.cases.do_not_format_me; + +import java.util.Optional; + +public class LiteralsFirstInComparisonsCases { + + public boolean isHardcoded(String input) { + return "hardcoded".equals(input); + } + + public boolean isPresent(Optional optional) { + return !optional.isEmpty(); + } +} diff --git a/testlib/src/main/resources/java/cleanthat/MultipleMutators.dirty.java b/testlib/src/main/resources/java/cleanthat/MultipleMutators.dirty.java new file mode 100644 index 0000000000..8ac230cabc --- /dev/null +++ b/testlib/src/main/resources/java/cleanthat/MultipleMutators.dirty.java @@ -0,0 +1,14 @@ +package eu.solven.cleanthat.engine.java.refactorer.cases.do_not_format_me; + +import java.util.Optional; + +public class LiteralsFirstInComparisonsCases { + + public boolean isHardcoded(String input) { + return input.equals("hardcoded"); + } + + public boolean isPresent(Optional optional) { + return !optional.isEmpty(); + } +} From 69e8524c650a53eaae67df350a56bb389b0a8903 Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Wed, 8 Feb 2023 22:28:15 +0400 Subject: [PATCH 05/13] Move to 2.1 to fix issue with class detection --- lib/build.gradle | 5 +- .../spotless/java/CleanthatJavaStep.java | 2 +- .../java/JavaCleanthatRefactorerFuncTest.java | 28 ++++++++ .../java/JavaCleanthatRefactorerFuncTest.java | 72 +++++++++++++++++++ .../java/CleanthatJavaRefactorerTest.java | 2 +- 5 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 lib/src/test/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java create mode 100644 lib/src/testCleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java diff --git a/lib/build.gradle b/lib/build.gradle index 0e1c2086ea..cd6996a979 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -55,6 +55,7 @@ dependencies { testCommonImplementation "org.junit.jupiter:junit-jupiter:$VER_JUNIT" testCommonImplementation "org.assertj:assertj-core:$VER_ASSERTJ" testCommonImplementation "com.diffplug.durian:durian-testlib:$VER_DURIAN" + testCommonImplementation 'io.github.solven-eu.cleanthat:java:2.1' // used for pom sorting sortPomCompileOnly 'com.github.ekryd.sortpom:sortpom-sorter:3.0.0' @@ -102,7 +103,9 @@ dependencies { gsonCompileOnly 'com.google.code.gson:gson:2.10.1' - cleanthatCompileOnly 'io.github.solven-eu.cleanthat:java:2.0' + // TODO How can one add a test module like 'compatKtLint0Dot48Dot0'? + // cleanthatCompileAndTestOnly 'io.github.solven-eu.cleanthat:java:2.1' + cleanthatCompileOnly 'io.github.solven-eu.cleanthat:java:2.1' } // we'll hold the core lib to a high standard diff --git a/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java b/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java index 79434cba56..fe4966d873 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java +++ b/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java @@ -39,7 +39,7 @@ public final class CleanthatJavaStep { private static final String NAME = "cleanthat"; private static final String MAVEN_COORDINATE = "io.github.solven-eu.cleanthat:java"; - private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(11, "2.0"); + private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(11, "2.1"); // prevent direct instantiation private CleanthatJavaStep() {} diff --git a/lib/src/test/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java b/lib/src/test/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java new file mode 100644 index 0000000000..2f3cdc9646 --- /dev/null +++ b/lib/src/test/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java @@ -0,0 +1,28 @@ +/* + * Copyright 2023 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.java; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import eu.solven.cleanthat.engine.java.refactorer.JavaRefactorer; + +public class JavaCleanthatRefactorerFuncTest { + @Test + public void testMutatorsDetection() { + Assertions.assertThat(JavaRefactorer.getAllIncluded()).isNotEmpty(); + } +} diff --git a/lib/src/testCleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java b/lib/src/testCleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java new file mode 100644 index 0000000000..c811b51233 --- /dev/null +++ b/lib/src/testCleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2023 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.ktlint.compat; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +public class KtLintCompat0Dot48Dot0AdapterTest { + @Test + public void testDefaults(@TempDir Path path) throws IOException { + KtLintCompat0Dot48Dot0Adapter ktLintCompat0Dot48Dot0Adapter = new KtLintCompat0Dot48Dot0Adapter(); + String text = loadAndWriteText(path, "empty_class_body.kt"); + final Path filePath = Paths.get(path.toString(), "empty_class_body.kt"); + + Map userData = new HashMap<>(); + + Map editorConfigOverrideMap = new HashMap<>(); + + String formatted = ktLintCompat0Dot48Dot0Adapter.format(text, filePath, false, false, null, userData, editorConfigOverrideMap); + assertEquals("class empty_class_body\n", formatted); + } + + @Test + public void testEditorConfigCanDisable(@TempDir Path path) throws IOException { + KtLintCompat0Dot48Dot0Adapter ktLintCompat0Dot48Dot0Adapter = new KtLintCompat0Dot48Dot0Adapter(); + String text = loadAndWriteText(path, "fails_no_semicolons.kt"); + final Path filePath = Paths.get(path.toString(), "fails_no_semicolons.kt"); + + Map userData = new HashMap<>(); + + Map editorConfigOverrideMap = new HashMap<>(); + editorConfigOverrideMap.put("indent_style", "tab"); + editorConfigOverrideMap.put("ktlint_standard_no-semi", "disabled"); + // ktlint_filename is an invalid rule in ktlint 0.48.0 + editorConfigOverrideMap.put("ktlint_filename", "disabled"); + + String formatted = ktLintCompat0Dot48Dot0Adapter.format(text, filePath, false, false, null, userData, editorConfigOverrideMap); + assertEquals("class fails_no_semicolons {\n\tval i = 0;\n}\n", formatted); + } + + private static String loadAndWriteText(Path path, String name) throws IOException { + try (InputStream is = KtLintCompat0Dot48Dot0AdapterTest.class.getResourceAsStream("/" + name)) { + Files.copy(is, path.resolve(name)); + } + return new String(Files.readAllBytes(path.resolve(name)), StandardCharsets.UTF_8); + } + +} diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/CleanthatJavaRefactorerTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/CleanthatJavaRefactorerTest.java index c72aeb15b2..833b19dda8 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/CleanthatJavaRefactorerTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/CleanthatJavaRefactorerTest.java @@ -70,7 +70,7 @@ void testIncludeOnlyLiteralsFirstInComparisons() throws Exception { private void runTest(String dirtyPath, String cleanPath) throws Exception { String path = "src/main/java/test.java"; setFile(path).toResource("java/cleanthat/" + dirtyPath); - Assertions.assertThat(mavenRunner().withArguments("spotless:apply -X").runNoError().stdOutUtf8()).isEmpty(); + Assertions.assertThat(mavenRunner().withArguments("spotless:apply").withRemoteDebug(21654).runNoError().stdOutUtf8()).doesNotContain("[ERROR]"); assertFile(path).sameAsResource("java/cleanthat/" + cleanPath); } } From bcab5b4abb044d6ba9b730b56b99c74cd4408453 Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Wed, 8 Feb 2023 22:32:23 +0400 Subject: [PATCH 06/13] Improve documentation and changelog ref --- .../spotless/glue/java/JavaCleanthatRefactorerFunc.java | 4 ++++ .../java/com/diffplug/spotless/java/CleanthatJavaStep.java | 1 + plugin-maven/README.md | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java index 5ec54e69be..7432ebf3f9 100644 --- a/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java +++ b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java @@ -30,6 +30,10 @@ import eu.solven.cleanthat.engine.java.refactorer.JavaRefactorerProperties; import eu.solven.cleanthat.formatter.LineEnding; +/** + * The glue for CleanThat: it is build over the version in build.gradle, but at runtime it will be executed over + * the version loaded in JarState, which is by default defined in com.diffplug.spotless.java.CleanthatJavaStep#JVM_SUPPORT + */ public class JavaCleanthatRefactorerFunc implements FormatterFunc { private static final Logger LOGGER = LoggerFactory.getLogger(JavaCleanthatRefactorerFunc.class); diff --git a/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java b/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java index fe4966d873..1cd032a304 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java +++ b/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java @@ -39,6 +39,7 @@ public final class CleanthatJavaStep { private static final String NAME = "cleanthat"; private static final String MAVEN_COORDINATE = "io.github.solven-eu.cleanthat:java"; + // CleanThat changelog is available at https://github.com/solven-eu/cleanthat/blob/master/CHANGES.MD private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(11, "2.1"); // prevent direct instantiation diff --git a/plugin-maven/README.md b/plugin-maven/README.md index 36d46b2386..b1b9098bfe 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -278,7 +278,7 @@ These mechanisms already exist for the Gradle plugin. ### Cleanthat -[homepage](https://github.com/solven-eu/cleanthat). CleanThat enables automatic refactoring of Java code +[homepage](https://github.com/solven-eu/cleanthat). CleanThat enables automatic refactoring of Java code. [ChangeLog](https://github.com/solven-eu/cleanthat/blob/master/CHANGES.MD) ```xml From a5c8b4da6975b69e5d62ea29246a422a269610e1 Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Wed, 8 Feb 2023 23:06:46 +0400 Subject: [PATCH 07/13] Fix classLoader issue --- .../java/JavaCleanthatRefactorerFunc.java | 19 +++++++++++++++++++ .../java/CleanthatJavaRefactorerTest.java | 15 +++++++++++++-- .../cleanthat/MultipleMutators.clean.java | 2 +- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java index 7432ebf3f9..c94fd3c7c4 100644 --- a/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java +++ b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java @@ -15,6 +15,7 @@ */ package com.diffplug.spotless.glue.java; +import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -25,6 +26,7 @@ import com.diffplug.spotless.FormatterFunc; import eu.solven.cleanthat.config.pojo.CleanthatEngineProperties; +import eu.solven.cleanthat.config.pojo.SourceCodeProperties; import eu.solven.cleanthat.engine.java.IJdkVersionConstants; import eu.solven.cleanthat.engine.java.refactorer.JavaRefactorer; import eu.solven.cleanthat.engine.java.refactorer.JavaRefactorerProperties; @@ -53,8 +55,25 @@ public JavaCleanthatRefactorerFunc() { @Override public String apply(String input) throws Exception { + // https://stackoverflow.com/questions/1771679/difference-between-threads-context-class-loader-and-normal-classloader + ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); + try { + // Ensure CleanThat main Thread has its custom classLoader while executing its refactoring + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + return doApply(input); + } finally { + // Restore the originalClassLoader + Thread.currentThread().setContextClassLoader(originalClassLoader); + } + } + + private String doApply(String input) throws InterruptedException, IOException { + // call some API that uses reflection without taking ClassLoader param CleanthatEngineProperties engineProperties = CleanthatEngineProperties.builder().engineVersion(jdkVersion).build(); + // Spotless will push us LF content + engineProperties.setSourceCode(SourceCodeProperties.builder().lineEnding(LineEnding.LF).build()); + JavaRefactorerProperties refactorerProperties = new JavaRefactorerProperties(); refactorerProperties.setIncluded(included); diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/CleanthatJavaRefactorerTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/CleanthatJavaRefactorerTest.java index 833b19dda8..edb7a69cd5 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/CleanthatJavaRefactorerTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/CleanthatJavaRefactorerTest.java @@ -35,11 +35,21 @@ void testLiteralsFirstInComparisons() throws Exception { } @Test - void testMultipleMutators() throws Exception { + void testMultipleMutators_defaultIsJdk7() throws Exception { writePomWithJavaSteps( "", ""); + runTest("MultipleMutators.dirty.java", "MultipleMutators.clean.onlyLiteralsFirst.java"); + } + + @Test + void testMultipleMutators_Jdk11IntroducedOptionalisPresent() throws Exception { + writePomWithJavaSteps( + "", + "11", + ""); + runTest("MultipleMutators.dirty.java", "MultipleMutators.clean.java"); } @@ -70,7 +80,8 @@ void testIncludeOnlyLiteralsFirstInComparisons() throws Exception { private void runTest(String dirtyPath, String cleanPath) throws Exception { String path = "src/main/java/test.java"; setFile(path).toResource("java/cleanthat/" + dirtyPath); - Assertions.assertThat(mavenRunner().withArguments("spotless:apply").withRemoteDebug(21654).runNoError().stdOutUtf8()).doesNotContain("[ERROR]"); + // .withRemoteDebug(21654) + Assertions.assertThat(mavenRunner().withArguments("spotless:apply").runNoError().stdOutUtf8()).doesNotContain("[ERROR]"); assertFile(path).sameAsResource("java/cleanthat/" + cleanPath); } } diff --git a/testlib/src/main/resources/java/cleanthat/MultipleMutators.clean.java b/testlib/src/main/resources/java/cleanthat/MultipleMutators.clean.java index 0829602dc1..318e1efa15 100644 --- a/testlib/src/main/resources/java/cleanthat/MultipleMutators.clean.java +++ b/testlib/src/main/resources/java/cleanthat/MultipleMutators.clean.java @@ -5,7 +5,7 @@ public class LiteralsFirstInComparisonsCases { public boolean isHardcoded(String input) { - return input.equals("hardcoded"); + return "hardcoded".equals(input); } public boolean isPresent(Optional optional) { From 14122d0f9254c570c4fb894560db91d6b681a528 Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Wed, 8 Feb 2023 23:35:47 +0400 Subject: [PATCH 08/13] Add Gradle compatibility --- .../gradle/spotless/JavaExtension.java | 74 ++++++++++++++++++- .../CleanthatJavaIntegrationTest.java | 42 +++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 plugin-gradle/src/test/java/com/diffplug/gradle/spotless/CleanthatJavaIntegrationTest.java diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java index 0f5926ec66..e65d0601e7 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import com.diffplug.spotless.extra.EclipseBasedStepBuilder; import com.diffplug.spotless.extra.java.EclipseJdtFormatterStep; import com.diffplug.spotless.generic.LicenseHeaderStep; +import com.diffplug.spotless.java.CleanthatJavaStep; import com.diffplug.spotless.java.FormatAnnotationsStep; import com.diffplug.spotless.java.GoogleJavaFormatStep; import com.diffplug.spotless.java.ImportOrderStep; @@ -270,6 +271,77 @@ private FormatterStep createStep() { } } + /** Apply CleanThat refactoring rules. */ + public CleanthatJavaConfig cleanthat() { + return new CleanthatJavaConfig(); + } + + public class CleanthatJavaConfig { + private String groupArtifact = CleanthatJavaStep.defaultGroupArtifact(); + + private String version = CleanthatJavaStep.defaultVersion(); + + private String sourceJdk = CleanthatJavaStep.defaultJdkVersion(); + + private List mutators = CleanthatJavaStep.defaultMutators(); + + private List excludedMutators = CleanthatJavaStep.defaultExcludedMutators(); + + CleanthatJavaConfig() { + addStep(createStep()); + } + + public CleanthatJavaConfig groupArtifact(String groupArtifact) { + Objects.requireNonNull(groupArtifact); + this.groupArtifact = groupArtifact; + replaceStep(createStep()); + return this; + } + + public CleanthatJavaConfig version(String version) { + Objects.requireNonNull(version); + this.version = version; + replaceStep(createStep()); + return this; + } + + public CleanthatJavaConfig sourceJdk(String sourceJdk) { + Objects.requireNonNull(sourceJdk); + this.sourceJdk = sourceJdk; + replaceStep(createStep()); + return this; + } + + // Especially useful to clear default mutators + public CleanthatJavaConfig clearMutators() { + this.mutators.clear(); + replaceStep(createStep()); + return this; + } + + // The fully qualified name of a class implementing eu.solven.cleanthat.engine.java.refactorer.meta.IMutator + // or '*' to include all default mutators + public CleanthatJavaConfig addMutator(String mutator) { + this.mutators.add(mutator); + replaceStep(createStep()); + return this; + } + + // useful to exclude a mutator amongst the default list of mutators + public CleanthatJavaConfig excludeMutator(String mutator) { + this.excludedMutators.add(mutator); + replaceStep(createStep()); + return this; + } + + private FormatterStep createStep() { + return CleanthatJavaStep.create( + groupArtifact, + version, + sourceJdk, mutators, excludedMutators, provisioner()); + } + } + /** If the user hasn't specified the files yet, we'll assume he/she means all of the java files. */ @Override protected void setupTask(SpotlessTask task) { diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/CleanthatJavaIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/CleanthatJavaIntegrationTest.java new file mode 100644 index 0000000000..2b3ede1472 --- /dev/null +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/CleanthatJavaIntegrationTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022-2023 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.gradle.spotless; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +class CleanthatJavaIntegrationTest extends GradleIntegrationHarness { + @Test + void integration() throws IOException { + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "", + "spotless {", + " java {", + " target file('test.java')", + " cleanthat().sourceJdk('11')", + " }", + "}"); + + setFile("test.java").toResource("java/cleanthat/MultipleMutators.dirty.java"); + gradleRunner().withArguments("spotlessApply").build(); + assertFile("test.java").sameAsResource("java/cleanthat/MultipleMutators.clean.java"); + } +} From cfd07b06952840df641eea1c59d6f495e86544e6 Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Wed, 8 Feb 2023 23:44:50 +0400 Subject: [PATCH 09/13] Improve Gradle integration --- .../spotless/java/CleanthatJavaStep.java | 6 ++--- plugin-gradle/CHANGES.md | 2 ++ plugin-gradle/README.md | 22 ++++++++++++++++++- .../gradle/spotless/JavaExtension.java | 8 +++---- plugin-maven/CHANGES.md | 2 +- .../spotless/maven/java/CleanthatJava.java | 2 +- 6 files changed, 32 insertions(+), 10 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java b/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java index 1cd032a304..b5cc5452d1 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java +++ b/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java @@ -52,10 +52,10 @@ public static FormatterStep create(Provisioner provisioner) { /** Creates a step which apply default CleanThat mutators. */ public static FormatterStep create(String version, Provisioner provisioner) { - return create(MAVEN_COORDINATE, version, defaultJdkVersion(), defaultExcludedMutators(), defaultMutators(), provisioner); + return create(MAVEN_COORDINATE, version, defaultSourceJdk(), defaultExcludedMutators(), defaultMutators(), provisioner); } - public static String defaultJdkVersion() { + public static String defaultSourceJdk() { // see IJdkVersionConstants.JDK_7 // https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#source // 1.7 is the default for 'maven-compiler-plugin' since 3.9.0 @@ -114,7 +114,7 @@ static final class JavaRefactorerState implements Serializable { final List excluded; JavaRefactorerState(String stepName, String version, Provisioner provisioner) throws IOException { - this(stepName, MAVEN_COORDINATE, version, defaultJdkVersion(), defaultExcludedMutators(), defaultMutators(), provisioner); + this(stepName, MAVEN_COORDINATE, version, defaultSourceJdk(), defaultExcludedMutators(), defaultMutators(), provisioner); } JavaRefactorerState(String stepName, diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index e3234584b8..1a5458c436 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`). ## [Unreleased] +### Added +* CleanThat Java Refactorer ([#1560](https://github.com/diffplug/spotless/pull/1560)) ## [6.14.1] - 2023-02-05 ### Fixed diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index f61280bc7e..c0f8adebfb 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -54,7 +54,7 @@ Spotless supports all of Gradle's built-in performance features (incremental bui - [**Quickstart**](#quickstart) - [Requirements](#requirements) - **Languages** - - [Java](#java) ([google-java-format](#google-java-format), [eclipse jdt](#eclipse-jdt), [clang-format](#clang-format), [prettier](#prettier), [palantir-java-format](#palantir-java-format), [formatAnnotations](#formatAnnotations)) + - [Java](#java) ([google-java-format](#google-java-format), [eclipse jdt](#eclipse-jdt), [clang-format](#clang-format), [prettier](#prettier), [palantir-java-format](#palantir-java-format), [formatAnnotations](#formatAnnotations), [cleanthat](#cleanthat)) - [Groovy](#groovy) ([eclipse groovy](#eclipse-groovy)) - [Kotlin](#kotlin) ([ktfmt](#ktfmt), [ktlint](#ktlint), [diktat](#diktat), [prettier](#prettier)) - [Scala](#scala) ([scalafmt](#scalafmt)) @@ -154,6 +154,9 @@ spotless { removeUnusedImports() + // Cleanthat will refactor your code, but it may break your style: apply it before your formatter + cleanthat() // has its own section below + // Choose one of these formatters. googleJavaFormat() // has its own section below eclipse() // has its own section below @@ -257,6 +260,23 @@ You can use `addTypeAnnotation()` and `removeTypeAnnotation()` to override its d You can make a pull request to add new annotations to Spotless's default list. +### cleanthat + +[homepage](https://github.com/solven-eu/cleanthat). CleanThat enables automatic refactoring of Java code. [ChangeLog](https://github.com/solven-eu/cleanthat/blob/master/CHANGES.MD) + +```gradle +spotless { + java { + cleanthat() + // optional: you can specify a specific version and/or config file + cleanthat() + .groupArtifact('1.7') // default is 'io.github.solven-eu.cleanthat:java' + .version('2.1') // You may force a past of -SNAPSHOT + .sourceCompatibility('1.7') // default is '1.7' + .addMutator('your.custom.MagicMutator') + .excludeMutator('UseCollectionIsEmpty') +``` + diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java index e65d0601e7..6e9f6de005 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java @@ -281,7 +281,7 @@ public class CleanthatJavaConfig { private String version = CleanthatJavaStep.defaultVersion(); - private String sourceJdk = CleanthatJavaStep.defaultJdkVersion(); + private String sourceJdk = CleanthatJavaStep.defaultSourceJdk(); private List mutators = CleanthatJavaStep.defaultMutators(); @@ -305,9 +305,9 @@ public CleanthatJavaConfig version(String version) { return this; } - public CleanthatJavaConfig sourceJdk(String sourceJdk) { - Objects.requireNonNull(sourceJdk); - this.sourceJdk = sourceJdk; + public CleanthatJavaConfig sourceCompatibility(String jdkVersion) { + Objects.requireNonNull(jdkVersion); + this.sourceJdk = jdkVersion; replaceStep(createStep()); return this; } diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index fc276f7c8d..a13353e68e 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -4,7 +4,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added -* CleanThat Java Refactorer ([#???](https://github.com/diffplug/spotless/pull/???)) +* CleanThat Java Refactorer ([#1560](https://github.com/diffplug/spotless/pull/1560)) ## [2.32.0] - 2023-02-05 ### Added diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java index 7460ab67b9..d7dd1f2530 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java @@ -33,7 +33,7 @@ public class CleanthatJava implements FormatterStepFactory { // https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#source @Parameter(property = "maven.compiler.source") - private String sourceJdk = CleanthatJavaStep.defaultJdkVersion(); + private String sourceJdk = CleanthatJavaStep.defaultSourceJdk(); @Parameter private List mutators = CleanthatJavaStep.defaultMutators(); From 374acb6fec5484f92a1119e05e3a80e9f92a1bc0 Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Thu, 9 Feb 2023 00:01:28 +0400 Subject: [PATCH 10/13] Fix unitTests --- README.md | 2 +- lib/build.gradle | 9 ++- .../java/JavaCleanthatRefactorerFuncTest.java | 72 ------------------- .../glue/java/JavaCleanthatRefactorerFuncTest | 28 ++++++++ .../CleanthatJavaIntegrationTest.java | 2 +- 5 files changed, 37 insertions(+), 76 deletions(-) delete mode 100644 lib/src/testCleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java create mode 100644 lib/src/testCompatCleanthat2Dot1/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest diff --git a/README.md b/README.md index 5f59368677..420c13ab53 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ lib('java.PalantirJavaFormatStep') +'{{yes}} | {{yes}} lib('java.RemoveUnusedImportsStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', extra('java.EclipseJdtFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |', lib('java.FormatAnnotationsStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', -lib('java.CleanthatJavaStep') +'{{no}} | {{yes}} | {{no}} | {{no}} |', +lib('java.CleanthatJavaStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('json.gson.GsonStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('json.JacksonJsonStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('json.JsonSimpleStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', diff --git a/lib/build.gradle b/lib/build.gradle index cd6996a979..154b2acd6a 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -40,6 +40,12 @@ versionCompatibility { ] targetSourceSetName = 'ktlint' } + namespaces.register('Cleanthat') { + versions = [ + '2.1', + ] + targetSourceSetName = 'cleanthat' + } } } @@ -103,9 +109,8 @@ dependencies { gsonCompileOnly 'com.google.code.gson:gson:2.10.1' - // TODO How can one add a test module like 'compatKtLint0Dot48Dot0'? - // cleanthatCompileAndTestOnly 'io.github.solven-eu.cleanthat:java:2.1' cleanthatCompileOnly 'io.github.solven-eu.cleanthat:java:2.1' + compatCleanthat2Dot1CompileAndTestOnly 'io.github.solven-eu.cleanthat:java:2.1' } // we'll hold the core lib to a high standard diff --git a/lib/src/testCleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java b/lib/src/testCleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java deleted file mode 100644 index c811b51233..0000000000 --- a/lib/src/testCleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2023 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.ktlint.compat; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -public class KtLintCompat0Dot48Dot0AdapterTest { - @Test - public void testDefaults(@TempDir Path path) throws IOException { - KtLintCompat0Dot48Dot0Adapter ktLintCompat0Dot48Dot0Adapter = new KtLintCompat0Dot48Dot0Adapter(); - String text = loadAndWriteText(path, "empty_class_body.kt"); - final Path filePath = Paths.get(path.toString(), "empty_class_body.kt"); - - Map userData = new HashMap<>(); - - Map editorConfigOverrideMap = new HashMap<>(); - - String formatted = ktLintCompat0Dot48Dot0Adapter.format(text, filePath, false, false, null, userData, editorConfigOverrideMap); - assertEquals("class empty_class_body\n", formatted); - } - - @Test - public void testEditorConfigCanDisable(@TempDir Path path) throws IOException { - KtLintCompat0Dot48Dot0Adapter ktLintCompat0Dot48Dot0Adapter = new KtLintCompat0Dot48Dot0Adapter(); - String text = loadAndWriteText(path, "fails_no_semicolons.kt"); - final Path filePath = Paths.get(path.toString(), "fails_no_semicolons.kt"); - - Map userData = new HashMap<>(); - - Map editorConfigOverrideMap = new HashMap<>(); - editorConfigOverrideMap.put("indent_style", "tab"); - editorConfigOverrideMap.put("ktlint_standard_no-semi", "disabled"); - // ktlint_filename is an invalid rule in ktlint 0.48.0 - editorConfigOverrideMap.put("ktlint_filename", "disabled"); - - String formatted = ktLintCompat0Dot48Dot0Adapter.format(text, filePath, false, false, null, userData, editorConfigOverrideMap); - assertEquals("class fails_no_semicolons {\n\tval i = 0;\n}\n", formatted); - } - - private static String loadAndWriteText(Path path, String name) throws IOException { - try (InputStream is = KtLintCompat0Dot48Dot0AdapterTest.class.getResourceAsStream("/" + name)) { - Files.copy(is, path.resolve(name)); - } - return new String(Files.readAllBytes(path.resolve(name)), StandardCharsets.UTF_8); - } - -} diff --git a/lib/src/testCompatCleanthat2Dot1/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest b/lib/src/testCompatCleanthat2Dot1/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest new file mode 100644 index 0000000000..2f3cdc9646 --- /dev/null +++ b/lib/src/testCompatCleanthat2Dot1/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest @@ -0,0 +1,28 @@ +/* + * Copyright 2023 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.java; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import eu.solven.cleanthat.engine.java.refactorer.JavaRefactorer; + +public class JavaCleanthatRefactorerFuncTest { + @Test + public void testMutatorsDetection() { + Assertions.assertThat(JavaRefactorer.getAllIncluded()).isNotEmpty(); + } +} diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/CleanthatJavaIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/CleanthatJavaIntegrationTest.java index 2b3ede1472..a754b963f2 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/CleanthatJavaIntegrationTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/CleanthatJavaIntegrationTest.java @@ -31,7 +31,7 @@ void integration() throws IOException { "spotless {", " java {", " target file('test.java')", - " cleanthat().sourceJdk('11')", + " cleanthat().sourceCompatibility('11')", " }", "}"); From 8fbbb7165a840fb8d5435266617a342e8fd21846 Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Thu, 9 Feb 2023 00:02:25 +0400 Subject: [PATCH 11/13] Remove useless dependency --- lib/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/build.gradle b/lib/build.gradle index 154b2acd6a..21181cd54b 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -61,7 +61,6 @@ dependencies { testCommonImplementation "org.junit.jupiter:junit-jupiter:$VER_JUNIT" testCommonImplementation "org.assertj:assertj-core:$VER_ASSERTJ" testCommonImplementation "com.diffplug.durian:durian-testlib:$VER_DURIAN" - testCommonImplementation 'io.github.solven-eu.cleanthat:java:2.1' // used for pom sorting sortPomCompileOnly 'com.github.ekryd.sortpom:sortpom-sorter:3.0.0' From 640a1c20426f1f5ef114a8b15176bf618d6b4d0d Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Thu, 9 Feb 2023 00:08:01 +0400 Subject: [PATCH 12/13] Remove irrelevant test file --- README.md | 2 +- .../java/JavaCleanthatRefactorerFuncTest.java | 28 ------------------- 2 files changed, 1 insertion(+), 29 deletions(-) delete mode 100644 lib/src/test/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java diff --git a/README.md b/README.md index 420c13ab53..662c624bdf 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ lib('yaml.JacksonYamlStep') +'{{yes}} | {{yes}} | [`java.RemoveUnusedImportsStep`](lib/src/main/java/com/diffplug/spotless/java/RemoveUnusedImportsStep.java) | :+1: | :+1: | :+1: | :white_large_square: | | [`java.EclipseJdtFormatterStep`](lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java) | :+1: | :+1: | :+1: | :white_large_square: | | [`java.FormatAnnotationsStep`](lib/src/main/java/com/diffplug/spotless/java/FormatAnnotationsStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | -| [`java.CleanthatJavaStep`](lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java) | :white_large_square: | :+1: | :white_large_square: | :white_large_square: | +| [`java.CleanthatJavaStep`](lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | | [`json.gson.GsonStep`](lib/src/main/java/com/diffplug/spotless/json/gson/GsonStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | | [`json.JacksonJsonStep`](lib/src/main/java/com/diffplug/spotless/json/JacksonJsonStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | | [`json.JsonSimpleStep`](lib/src/main/java/com/diffplug/spotless/json/JsonSimpleStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | diff --git a/lib/src/test/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java b/lib/src/test/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java deleted file mode 100644 index 2f3cdc9646..0000000000 --- a/lib/src/test/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2023 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.java; - -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; - -import eu.solven.cleanthat.engine.java.refactorer.JavaRefactorer; - -public class JavaCleanthatRefactorerFuncTest { - @Test - public void testMutatorsDetection() { - Assertions.assertThat(JavaRefactorer.getAllIncluded()).isNotEmpty(); - } -} From fdee03ce774ceff7ef92b3d4db680b0e361ed0fd Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Thu, 9 Feb 2023 00:25:00 +0400 Subject: [PATCH 13/13] Fix extention of unitTest --- ...RefactorerFuncTest => JavaCleanthatRefactorerFuncTest.java} | 0 plugin-maven/README.md | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) rename lib/src/testCompatCleanthat2Dot1/java/com/diffplug/spotless/glue/java/{JavaCleanthatRefactorerFuncTest => JavaCleanthatRefactorerFuncTest.java} (100%) diff --git a/lib/src/testCompatCleanthat2Dot1/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest b/lib/src/testCompatCleanthat2Dot1/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java similarity index 100% rename from lib/src/testCompatCleanthat2Dot1/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest rename to lib/src/testCompatCleanthat2Dot1/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFuncTest.java diff --git a/plugin-maven/README.md b/plugin-maven/README.md index b1b9098bfe..f59a1d9004 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -182,6 +182,7 @@ any other maven phase (i.e. compile) then it can be configured as below; src/test/java/**/*.java + @@ -285,7 +286,7 @@ These mechanisms already exist for the Gradle plugin. 2.0 ${maven.compiler.source} - * + * LiteralsFirstInComparisons