diff --git a/build.gradle.kts b/build.gradle.kts index 99a1d51a86..8fbf549af1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -58,8 +58,11 @@ dependencies { testRuntimeOnly("com.fasterxml.jackson.core:jackson-databind") testRuntimeOnly("com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider") testRuntimeOnly("com.fasterxml.jackson.module:jackson-module-jaxb-annotations") + testRuntimeOnly("commons-logging:commons-logging:1.3.2") + testRuntimeOnly("org.apache.logging.log4j:log4j-api:2.23.1") testRuntimeOnly("org.apache.johnzon:johnzon-core:1.2.18") testRuntimeOnly("org.codehaus.groovy:groovy:latest.release") + testRuntimeOnly("org.jboss.logging:jboss-logging:3.6.0.Final") testRuntimeOnly("jakarta.annotation:jakarta.annotation-api:2.1.1") testRuntimeOnly("org.springframework:spring-core:6.1.13") testRuntimeOnly("com.google.code.findbugs:jsr305:3.0.2") diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/log/LogVisitor.java b/src/main/java/org/openrewrite/java/migrate/lombok/log/LogVisitor.java new file mode 100644 index 0000000000..9af9224231 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/lombok/log/LogVisitor.java @@ -0,0 +1,106 @@ +/* + * Copyright 2021 the original author or authors. + *

+ * 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 + *

+ * https://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 org.openrewrite.java.migrate.lombok.log; + +import lombok.EqualsAndHashCode; +import org.jspecify.annotations.Nullable; +import org.openrewrite.ExecutionContext; +import org.openrewrite.java.*; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.TypeUtils; + +import static java.util.Comparator.comparing; + +@EqualsAndHashCode(callSuper = false) +class LogVisitor extends JavaIsoVisitor { + + private final String logType; + private final String factoryType; + private final MethodMatcher factoryMethodMatcher; + private final String logAnnotation; + @Nullable + private final String fieldName; + + LogVisitor(String logType, String factoryMethodPattern, String logAnnotation, @Nullable String fieldName) { + this.logType = logType; + this.factoryType = factoryMethodPattern.substring(0, factoryMethodPattern.indexOf(' ')); + this.factoryMethodMatcher = new MethodMatcher(factoryMethodPattern); + this.logAnnotation = logAnnotation; + this.fieldName = fieldName; + } + + @Override + public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { + J.ClassDeclaration visitClassDeclaration = super.visitClassDeclaration(classDecl, ctx); + if (visitClassDeclaration != classDecl) { + maybeRemoveImport(logType); + maybeRemoveImport(factoryType); + maybeAddImport(logAnnotation); + return JavaTemplate + .builder("@" + logAnnotation.substring(logAnnotation.lastIndexOf('.') + 1) + "\n") + .javaParser(JavaParser.fromJavaVersion().classpath("lombok")) + .imports(logAnnotation) + .build() + .apply( + updateCursor(visitClassDeclaration), + visitClassDeclaration.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName))); + } + return classDecl; + } + + @Override + public J.@Nullable VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) { + if (!multiVariable.hasModifier(J.Modifier.Type.Private) || + !multiVariable.hasModifier(J.Modifier.Type.Static) || + !multiVariable.hasModifier(J.Modifier.Type.Final) || + multiVariable.getVariables().size() != 1 || + !TypeUtils.isAssignableTo(logType, multiVariable.getType())) { + return multiVariable; + } + + // name needs to match the name of the field that lombok creates + J.VariableDeclarations.NamedVariable var = multiVariable.getVariables().get(0); + if (fieldName != null && !fieldName.equals(var.getSimpleName())) { + return multiVariable; + } + + if (!factoryMethodMatcher.matches(var.getInitializer())) { + return multiVariable; + } + + J.ClassDeclaration classDeclaration = getCursor().firstEnclosing(J.ClassDeclaration.class); + if (classDeclaration == null || classDeclaration.getType() == null) { + return multiVariable; + } + + J.MethodInvocation methodCall = (J.MethodInvocation) var.getInitializer(); + if (methodCall.getArguments().size() != 1 || + !getFactoryParameter(classDeclaration.getSimpleName()) + .equals(methodCall.getArguments().get(0).toString())) { + return multiVariable; + } + + if (!"log".equals(var.getSimpleName())) { + doAfterVisit(new ChangeFieldName<>(classDeclaration.getType().getFullyQualifiedName(), var.getSimpleName(), "log")); + } + + return null; + } + + protected String getFactoryParameter(String className) { + return className + ".class"; + } +} diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/log/UseCommonsLog.java b/src/main/java/org/openrewrite/java/migrate/lombok/log/UseCommonsLog.java new file mode 100644 index 0000000000..a1c10a0f90 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/lombok/log/UseCommonsLog.java @@ -0,0 +1,55 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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 org.openrewrite.java.migrate.lombok.log; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.jspecify.annotations.Nullable; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Option; +import org.openrewrite.TreeVisitor; + +@Value +@EqualsAndHashCode(callSuper = false) +public class UseCommonsLog extends UseLogRecipeTemplate { + + @Override + public String getDisplayName() { + return getDisplayName("@CommonsLog"); + } + + @Override + public String getDescription() { + return getDescription("@CommonsLog", "org.apache.commons.logging.Log"); + } + + @Option(displayName = "Name of the log field", + description = FIELD_NAME_DESCRIPTION, + example = "LOGGER", + required = false) + @Nullable + String fieldName; + + @Override + public TreeVisitor getVisitor() { + return new LogVisitor( + "org.apache.commons.logging.Log", + "org.apache.commons.logging.LogFactory getLog(..)", + "lombok.extern.apachecommons.CommonsLog", + fieldName); + } + +} diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/log/UseJBossLog.java b/src/main/java/org/openrewrite/java/migrate/lombok/log/UseJBossLog.java new file mode 100644 index 0000000000..6acec98ff3 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/lombok/log/UseJBossLog.java @@ -0,0 +1,55 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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 org.openrewrite.java.migrate.lombok.log; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.jspecify.annotations.Nullable; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Option; +import org.openrewrite.TreeVisitor; + +@Value +@EqualsAndHashCode(callSuper = false) +public class UseJBossLog extends UseLogRecipeTemplate { + + @Override + public String getDisplayName() { + return getDisplayName("@JBossLog"); + } + + @Override + public String getDescription() { + return getDescription("@JBossLog", "org.jboss.logging.Logger"); + } + + @Option(displayName = "Name of the log field", + description = FIELD_NAME_DESCRIPTION, + example = "LOGGER", + required = false) + @Nullable + String fieldName; + + @Override + public TreeVisitor getVisitor() { + return new LogVisitor( + "org.jboss.logging.Logger", + "org.jboss.logging.Logger getLogger(..)", + "lombok.extern.jbosslog.JBossLog", + fieldName); + } + +} diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/log/UseLog.java b/src/main/java/org/openrewrite/java/migrate/lombok/log/UseLog.java new file mode 100644 index 0000000000..cc724222eb --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/lombok/log/UseLog.java @@ -0,0 +1,60 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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 org.openrewrite.java.migrate.lombok.log; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.jspecify.annotations.Nullable; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Option; +import org.openrewrite.TreeVisitor; + +@Value +@EqualsAndHashCode(callSuper = false) +public class UseLog extends UseLogRecipeTemplate { + + @Override + public String getDisplayName() { + return getDisplayName("@Log"); + } + + @Override + public String getDescription() { + return getDescription("@Log", "java.util.logging.Logger"); + } + + @Option(displayName = "Name of the log field", + description = FIELD_NAME_DESCRIPTION, + example = "LOGGER", + required = false) + @Nullable + String fieldName; + + @Override + public TreeVisitor getVisitor() { + return new LogVisitor( + "java.util.logging.Logger", + "java.util.logging.Logger getLogger(String)", + "lombok.extern.java.Log", + fieldName) { + + @Override + protected String getFactoryParameter(String className) { + return className + ".class.getName()"; + } + }; + } +} diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/log/UseLog4j2.java b/src/main/java/org/openrewrite/java/migrate/lombok/log/UseLog4j2.java new file mode 100644 index 0000000000..f1539a58ae --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/lombok/log/UseLog4j2.java @@ -0,0 +1,55 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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 org.openrewrite.java.migrate.lombok.log; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.jspecify.annotations.Nullable; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Option; +import org.openrewrite.TreeVisitor; + +@Value +@EqualsAndHashCode(callSuper = false) +public class UseLog4j2 extends UseLogRecipeTemplate { + + @Override + public String getDisplayName() { + return getDisplayName("@Log4j2"); + } + + @Override + public String getDescription() { + return getDescription("@Log4j2", "org.apache.logging.log4j.Logger"); + } + + @Option(displayName = "Name of the log field", + description = FIELD_NAME_DESCRIPTION, + example = "LOGGER", + required = false) + @Nullable + String fieldName; + + @Override + public TreeVisitor getVisitor() { + return new LogVisitor( + "org.apache.logging.log4j.Logger", + "org.apache.logging.log4j.LogManager getLogger(..)", + "lombok.extern.log4j.Log4j2", + fieldName); + } + +} diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/log/UseLogRecipeTemplate.java b/src/main/java/org/openrewrite/java/migrate/lombok/log/UseLogRecipeTemplate.java new file mode 100644 index 0000000000..37387cb3ef --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/lombok/log/UseLogRecipeTemplate.java @@ -0,0 +1,34 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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 org.openrewrite.java.migrate.lombok.log; + +import org.openrewrite.Recipe; + +abstract class UseLogRecipeTemplate extends Recipe { + + protected static final String FIELD_NAME_DESCRIPTION = "Name of the log field to replace. " + + "If not specified, the field name is not checked and any field that satisfies the other checks is converted."; + + protected String getDisplayName(String annotation) { + return String.format("Use `%s` instead of explicit fields", annotation); + } + + protected String getDescription(String annotation, String pathToLogger) { + //language=markdown + return String.format("Prefer the lombok annotation `%s` over explicitly written out `%s` fields.", annotation, pathToLogger); + } + +} diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/log/UseSlf4j.java b/src/main/java/org/openrewrite/java/migrate/lombok/log/UseSlf4j.java new file mode 100644 index 0000000000..239edd3601 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/lombok/log/UseSlf4j.java @@ -0,0 +1,54 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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 org.openrewrite.java.migrate.lombok.log; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.jspecify.annotations.Nullable; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Option; +import org.openrewrite.TreeVisitor; + +@Value +@EqualsAndHashCode(callSuper = false) +public class UseSlf4j extends UseLogRecipeTemplate { + + @Override + public String getDisplayName() { + return getDisplayName("@Slf4"); + } + + @Override + public String getDescription() { + return getDescription("@Slf4", "org.slf4j.Logger"); + } + + @Option(displayName = "Name of the log field", + description = FIELD_NAME_DESCRIPTION, + example = "LOGGER", + required = false) + @Nullable + String fieldName; + + @Override + public TreeVisitor getVisitor() { + return new LogVisitor("org.slf4j.Logger", + "org.slf4j.LoggerFactory getLogger(..)", + "lombok.extern.slf4j.Slf4j", + fieldName); + } + +} diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/log/package-info.java b/src/main/java/org/openrewrite/java/migrate/lombok/log/package-info.java new file mode 100644 index 0000000000..7ecaac351a --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/lombok/log/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * Licensed under the Moderne Source Available License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://docs.moderne.io/licensing/moderne-source-available-license + *

+ * 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. + */ +@NullMarked +@NonNullFields +package org.openrewrite.java.migrate.lombok.log; + +import org.jspecify.annotations.NullMarked; +import org.openrewrite.internal.lang.NonNullFields; diff --git a/src/main/resources/META-INF/rewrite/lombok.yml b/src/main/resources/META-INF/rewrite/lombok.yml index 554b8c427b..4dd3207b52 100644 --- a/src/main/resources/META-INF/rewrite/lombok.yml +++ b/src/main/resources/META-INF/rewrite/lombok.yml @@ -43,3 +43,16 @@ recipeList: oldFullyQualifiedTypeName: lombok.experimental.val newFullyQualifiedTypeName: lombok.val - org.openrewrite.java.migrate.lombok.LombokValToFinalVar + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.lombok.log.UseLombokLogAnnotations +displayName: Use Lombok logger annotations instead of explicit fields +description: >- + Applies all recipes that replace logger declarations with class level annotations. +recipeList: + - org.openrewrite.java.migrate.lombok.log.UseCommonsLog + - org.openrewrite.java.migrate.lombok.log.UseJBossLog + - org.openrewrite.java.migrate.lombok.log.UseLog + - org.openrewrite.java.migrate.lombok.log.UseLog4j2 + - org.openrewrite.java.migrate.lombok.log.UseSlf4j diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/log/ConvertAnyLogTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/log/ConvertAnyLogTest.java new file mode 100644 index 0000000000..6cbf558f3c --- /dev/null +++ b/src/test/java/org/openrewrite/java/migrate/lombok/log/ConvertAnyLogTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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 org.openrewrite.java.migrate.lombok.log; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class ConvertAnyLogTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResources("org.openrewrite.java.migrate.lombok.log.UseLombokLogAnnotations") + .parser(JavaParser.fromJavaVersion() + .classpath("slf4j-api", "log4j-api", "jboss-logging", "commons-logging")); + } + + @DocumentExample + @Test + void replaceAllLoggers() { + rewriteRun( + // language=java + java( + """ + class A { + private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(A.class); + } + """, + """ + import lombok.extern.slf4j.Slf4j; + + @Slf4j + class A { + } + """ + ), + // language=java + java( + """ + import org.apache.logging.log4j.Logger; + import org.apache.logging.log4j.LogManager; + class B { + private static final Logger log = LogManager.getLogger(B.class); + } + """, + """ + import lombok.extern.log4j.Log4j2; + + @Log4j2 + class B { + } + """ + ), + // language=java + java( + """ + import java.util.logging.Logger; + class C { + private static final Logger log = Logger.getLogger(C.class.getName()); + } + """, + """ + import lombok.extern.java.Log; + + @Log + class C { + } + """ + ), + // language=java + java( + """ + import org.jboss.logging.Logger; + class D { + private static final Logger log = Logger.getLogger(D.class); + } + """, + """ + import lombok.extern.jbosslog.JBossLog; + + @JBossLog + class D { + } + """ + ), + // language=java + java( + """ + import org.apache.commons.logging.Log; + import org.apache.commons.logging.LogFactory; + class E { + private static final Log log = LogFactory.getLog(E.class); + } + """, + """ + import lombok.extern.apachecommons.CommonsLog; + + @CommonsLog + class E { + } + """ + ) + ); + } +} diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/log/ConvertToLogAnnotationTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/log/ConvertToLogAnnotationTest.java new file mode 100644 index 0000000000..1af3f43911 --- /dev/null +++ b/src/test/java/org/openrewrite/java/migrate/lombok/log/ConvertToLogAnnotationTest.java @@ -0,0 +1,339 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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 org.openrewrite.java.migrate.lombok.log; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.openrewrite.DocumentExample; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class ConvertToLogAnnotationTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new UseSlf4j(null)) + .parser(JavaParser.fromJavaVersion() + .classpath("slf4j-api")); + } + + @DocumentExample + @Test + void replaceSlf4j() { + rewriteRun(// language=java + java( + """ + class A { + private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(A.class); + } + """, + """ + import lombok.extern.slf4j.Slf4j; + + @Slf4j + class A { + } + """ + ) + ); + } + + @Nested + class RenameField { + + @BeforeAll + static void setUp() { + System.setProperty("rewrite.lombok", "true"); + } + + @AfterAll + static void tearDown() { + System.clearProperty("rewrite.lombok"); + } + + @Test + void replaceSlf4jAndRenameFieldUsages() { + rewriteRun( + // language=java + java( + """ + class A { + private static final org.slf4j.Logger renamed = org.slf4j.LoggerFactory.getLogger(A.class); + + void test() { + renamed.info("test"); + } + } + """, + """ + import lombok.extern.slf4j.Slf4j; + + @Slf4j + class A { + + void test() { + log.info("test"); + } + } + """ + ) + ); + } + } + + @Test + void replaceSlf4jImportedType() { + rewriteRun(// language=java + java( + """ + import org.slf4j.Logger; + class A { + private static final Logger log = org.slf4j.LoggerFactory.getLogger(A.class); + } + """, + """ + import lombok.extern.slf4j.Slf4j; + + @Slf4j + class A { + } + """ + ) + ); + } + + @Test + void replaceSlf4jImportedLogger() { + rewriteRun(// language=java + java( + """ + import org.slf4j.LoggerFactory; + class A { + private static final org.slf4j.Logger log = LoggerFactory.getLogger(A.class); + } + """, + """ + import lombok.extern.slf4j.Slf4j; + + @Slf4j + class A { + } + """ + ) + ); + } + + @Test + void replaceSlf4jStaticallyImportedLogger() { + rewriteRun(// language=java + java( + """ + import static org.slf4j.LoggerFactory.*; + class A { + private static final org.slf4j.Logger log = getLogger(A.class); + } + """, + """ + import lombok.extern.slf4j.Slf4j; + + @Slf4j + class A { + } + """ + ) + ); + } + + @Test + void shouldNotReplaceWhenFieldNameDiffersFromSpecifiedName() { + rewriteRun( + spec -> spec.recipe(new UseSlf4j("log")) + .parser(JavaParser.fromJavaVersion() + .classpath("slf4j-api")), + + // language=java + java( + """ + class A { + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(A.class); + } + """ + ) + ); + } + + @ParameterizedTest + @ValueSource(strings = { + "public static final", + "protected static final", + "static final", + "private final", + "private static", + }) + void shouldNotReplaceWhenNotPrivateStaticFinal(String modifiers) { + rewriteRun( + spec -> spec.recipe(new UseSlf4j(null)) + .parser(JavaParser.fromJavaVersion() + .classpath("slf4j-api")), + + // language=java + java( + """ + class A { + %s org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(A.class); + } + """.formatted(modifiers) + ) + ); + } + + @Test + void replaceSlf4jWithPackage() { + rewriteRun(// language=java + java( + """ + package com.yourorg.yourapp; + class A { + private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(A.class); + } + """, + """ + package com.yourorg.yourapp; + + import lombok.extern.slf4j.Slf4j; + + @Slf4j + class A { + } + """ + ) + ); + } + + @Test + void replaceLog4j() { + rewriteRun( + spec -> spec.recipe(new UseLog4j2(null)) + .parser(JavaParser.fromJavaVersion() + .logCompilationWarningsAndErrors(true) + .classpath("log4j-api")), + // language=java + java( + """ + import org.apache.logging.log4j.Logger; + import org.apache.logging.log4j.LogManager; + class A { + private static final Logger log = LogManager.getLogger(A.class); + } + """, + """ + import lombok.extern.log4j.Log4j2; + + @Log4j2 + class A { + } + """ + ) + ); + } + + @Test + void replaceLog() { + rewriteRun( + spec -> spec.recipe(new UseLog(null)) + .parser(JavaParser.fromJavaVersion() + .logCompilationWarningsAndErrors(true)), + + // language=java + java( + """ + import java.util.logging.Logger; + class A { + private static final Logger log = Logger.getLogger(A.class.getName()); + } + """, + """ + import lombok.extern.java.Log; + + @Log + class A { + } + """ + ) + ); + } + + @Test + void replaceJbossLog() { + rewriteRun( + spec -> spec.recipe(new UseJBossLog(null)) + .parser(JavaParser.fromJavaVersion() + .logCompilationWarningsAndErrors(true) + .classpath("jboss-logging")), + // language=java + java( + """ + import org.jboss.logging.Logger; + class A { + private static final Logger log = Logger.getLogger(A.class); + } + """, + """ + import lombok.extern.jbosslog.JBossLog; + + @JBossLog + class A { + } + """ + ) + ); + } + + @Test + void replaceCommonsLog() { + rewriteRun( + spec -> spec.recipe(new UseCommonsLog(null)) + .parser(JavaParser.fromJavaVersion() + .logCompilationWarningsAndErrors(true) + .classpath("commons-logging")), + // language=java + java( + """ + import org.apache.commons.logging.Log; + import org.apache.commons.logging.LogFactory; + class A { + private static final Log log = LogFactory.getLog(A.class); + } + """, + """ + import lombok.extern.apachecommons.CommonsLog; + + @CommonsLog + class A { + } + """ + ) + ); + } + +}