/* * Copyright 2023-2024 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.java; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.Method; 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. * * @author Benoit Lacelle */ // https://github.com/diffplug/spotless/blob/main/CONTRIBUTING.md#how-to-add-a-new-formatterstep public final class CleanthatJavaStep implements java.io.Serializable { private static final long serialVersionUID = 1L; private static final String NAME = "cleanthat"; private static final String MAVEN_COORDINATE = "io.github.solven-eu.cleanthat:java"; /** * CleanThat changelog is available at here. */ private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(11, "2.16"); private final JarState.Promised jarState; private final String version; private final String sourceJdkVersion; private final List included; private final List excluded; private final boolean includeDraft; private CleanthatJavaStep(JarState.Promised jarState, String version, String sourceJdkVersion, List included, List excluded, boolean includeDraft) { this.jarState = jarState; this.version = version; this.sourceJdkVersion = sourceJdkVersion; this.included = included; this.excluded = excluded; this.includeDraft = includeDraft; } /** Creates a step that applies default CleanThat mutators. */ public static FormatterStep create(Provisioner provisioner) { return create(defaultVersion(), provisioner); } /** Creates a step that applies default CleanThat mutators. */ public static FormatterStep create(String version, Provisioner provisioner) { return create(MAVEN_COORDINATE, version, defaultSourceJdk(), defaultMutators(), defaultExcludedMutators(), defaultIncludeDraft(), provisioner); } 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 return "1.7"; } /** * By default, we include only safe and consensual mutators */ public static List defaultMutators() { // see ICleanthatStepParametersProperties.SAFE_AND_CONSENSUAL return List.of("SafeAndConsensual"); } public static List defaultExcludedMutators() { return List.of(); } public static boolean defaultIncludeDraft() { return false; } /** Creates a step that applies selected CleanThat mutators. */ public static FormatterStep create(String groupArtifact, String version, String sourceJdkVersion, List included, List excluded, boolean includeDraft, 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'. it was: " + groupArtifact); } Objects.requireNonNull(version, "version"); Objects.requireNonNull(provisioner, "provisioner"); return FormatterStep.create(NAME, new CleanthatJavaStep(JarState.promise(() -> JarState.from(groupArtifact + ":" + version, provisioner)), version, sourceJdkVersion, included, excluded, includeDraft), CleanthatJavaStep::equalityState, State::createFormat); } /** Get default formatter version */ public static String defaultVersion() { return Objects.requireNonNull(JVM_SUPPORT.getRecommendedFormatterVersion()); } public static String defaultGroupArtifact() { return MAVEN_COORDINATE; } private State equalityState() { return new State(jarState.get(), version, sourceJdkVersion, included, excluded, includeDraft); } private static final class State implements Serializable { private static final long serialVersionUID = 1L; private final JarState jarState; private final String version; private final String sourceJdkVersion; private final List included; private final List excluded; private final boolean includeDraft; State(JarState jarState, String version, String sourceJdkVersion, List included, List excluded, boolean includeDraft) { JVM_SUPPORT.assertFormatterSupported(version); ModuleHelper.doOpenInternalPackagesIfRequired(); this.jarState = jarState; this.version = version; this.sourceJdkVersion = sourceJdkVersion; this.included = included; this.excluded = excluded; this.includeDraft = includeDraft; } FormatterFunc createFormat() { ClassLoader classLoader = jarState.getClassLoader(); Object formatter; Method formatterMethod; try { Class formatterClazz = classLoader.loadClass("com.diffplug.spotless.glue.java.JavaCleanthatRefactorerFunc"); Constructor formatterConstructor = formatterClazz.getConstructor(String.class, List.class, List.class, boolean.class); formatter = formatterConstructor.newInstance(sourceJdkVersion, included, excluded, includeDraft); formatterMethod = formatterClazz.getMethod("apply", String.class); } catch (ReflectiveOperationException e) { throw new IllegalStateException("Issue executing the formatter", e); } return JVM_SUPPORT.suggestLaterVersionOnError(version, input -> (String) formatterMethod.invoke(formatter, input)); } } }