Skip to content

Commit 3ddf378

Browse files
timo-atimtebeek
andauthored
feat: use Lombok Log Annotations (#644)
* migrate recipe as-is * fix license * minor polish * rename recipes * add tests * Use `firstEnclosingOrThrow` to find class name, not messaging * Inline getLombokTemplate and visitors * Use TypeUtils and MethodMatcher * Verify modifier handling * Split methodPattern argument in constructor * Add dependencies as test runtime only * Drop unnecesary markdown hints * Move and rename declarative recipe * Rename usages of alternatively named logger fields * Use correct recipe in test --------- Co-authored-by: Tim te Beek <tim@moderne.io>
1 parent 46a5d0a commit 3ddf378

File tree

12 files changed

+917
-0
lines changed

12 files changed

+917
-0
lines changed

build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,11 @@ dependencies {
5858
testRuntimeOnly("com.fasterxml.jackson.core:jackson-databind")
5959
testRuntimeOnly("com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider")
6060
testRuntimeOnly("com.fasterxml.jackson.module:jackson-module-jaxb-annotations")
61+
testRuntimeOnly("commons-logging:commons-logging:1.3.2")
62+
testRuntimeOnly("org.apache.logging.log4j:log4j-api:2.23.1")
6163
testRuntimeOnly("org.apache.johnzon:johnzon-core:1.2.18")
6264
testRuntimeOnly("org.codehaus.groovy:groovy:latest.release")
65+
testRuntimeOnly("org.jboss.logging:jboss-logging:3.6.0.Final")
6366
testRuntimeOnly("jakarta.annotation:jakarta.annotation-api:2.1.1")
6467
testRuntimeOnly("org.springframework:spring-core:6.1.13")
6568
testRuntimeOnly("com.google.code.findbugs:jsr305:3.0.2")
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.lombok.log;
17+
18+
import lombok.EqualsAndHashCode;
19+
import org.jspecify.annotations.Nullable;
20+
import org.openrewrite.ExecutionContext;
21+
import org.openrewrite.java.*;
22+
import org.openrewrite.java.tree.J;
23+
import org.openrewrite.java.tree.TypeUtils;
24+
25+
import static java.util.Comparator.comparing;
26+
27+
@EqualsAndHashCode(callSuper = false)
28+
class LogVisitor extends JavaIsoVisitor<ExecutionContext> {
29+
30+
private final String logType;
31+
private final String factoryType;
32+
private final MethodMatcher factoryMethodMatcher;
33+
private final String logAnnotation;
34+
@Nullable
35+
private final String fieldName;
36+
37+
LogVisitor(String logType, String factoryMethodPattern, String logAnnotation, @Nullable String fieldName) {
38+
this.logType = logType;
39+
this.factoryType = factoryMethodPattern.substring(0, factoryMethodPattern.indexOf(' '));
40+
this.factoryMethodMatcher = new MethodMatcher(factoryMethodPattern);
41+
this.logAnnotation = logAnnotation;
42+
this.fieldName = fieldName;
43+
}
44+
45+
@Override
46+
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
47+
J.ClassDeclaration visitClassDeclaration = super.visitClassDeclaration(classDecl, ctx);
48+
if (visitClassDeclaration != classDecl) {
49+
maybeRemoveImport(logType);
50+
maybeRemoveImport(factoryType);
51+
maybeAddImport(logAnnotation);
52+
return JavaTemplate
53+
.builder("@" + logAnnotation.substring(logAnnotation.lastIndexOf('.') + 1) + "\n")
54+
.javaParser(JavaParser.fromJavaVersion().classpath("lombok"))
55+
.imports(logAnnotation)
56+
.build()
57+
.apply(
58+
updateCursor(visitClassDeclaration),
59+
visitClassDeclaration.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
60+
}
61+
return classDecl;
62+
}
63+
64+
@Override
65+
public J.@Nullable VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
66+
if (!multiVariable.hasModifier(J.Modifier.Type.Private) ||
67+
!multiVariable.hasModifier(J.Modifier.Type.Static) ||
68+
!multiVariable.hasModifier(J.Modifier.Type.Final) ||
69+
multiVariable.getVariables().size() != 1 ||
70+
!TypeUtils.isAssignableTo(logType, multiVariable.getType())) {
71+
return multiVariable;
72+
}
73+
74+
// name needs to match the name of the field that lombok creates
75+
J.VariableDeclarations.NamedVariable var = multiVariable.getVariables().get(0);
76+
if (fieldName != null && !fieldName.equals(var.getSimpleName())) {
77+
return multiVariable;
78+
}
79+
80+
if (!factoryMethodMatcher.matches(var.getInitializer())) {
81+
return multiVariable;
82+
}
83+
84+
J.ClassDeclaration classDeclaration = getCursor().firstEnclosing(J.ClassDeclaration.class);
85+
if (classDeclaration == null || classDeclaration.getType() == null) {
86+
return multiVariable;
87+
}
88+
89+
J.MethodInvocation methodCall = (J.MethodInvocation) var.getInitializer();
90+
if (methodCall.getArguments().size() != 1 ||
91+
!getFactoryParameter(classDeclaration.getSimpleName())
92+
.equals(methodCall.getArguments().get(0).toString())) {
93+
return multiVariable;
94+
}
95+
96+
if (!"log".equals(var.getSimpleName())) {
97+
doAfterVisit(new ChangeFieldName<>(classDeclaration.getType().getFullyQualifiedName(), var.getSimpleName(), "log"));
98+
}
99+
100+
return null;
101+
}
102+
103+
protected String getFactoryParameter(String className) {
104+
return className + ".class";
105+
}
106+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.lombok.log;
17+
18+
import lombok.EqualsAndHashCode;
19+
import lombok.Value;
20+
import org.jspecify.annotations.Nullable;
21+
import org.openrewrite.ExecutionContext;
22+
import org.openrewrite.Option;
23+
import org.openrewrite.TreeVisitor;
24+
25+
@Value
26+
@EqualsAndHashCode(callSuper = false)
27+
public class UseCommonsLog extends UseLogRecipeTemplate {
28+
29+
@Override
30+
public String getDisplayName() {
31+
return getDisplayName("@CommonsLog");
32+
}
33+
34+
@Override
35+
public String getDescription() {
36+
return getDescription("@CommonsLog", "org.apache.commons.logging.Log");
37+
}
38+
39+
@Option(displayName = "Name of the log field",
40+
description = FIELD_NAME_DESCRIPTION,
41+
example = "LOGGER",
42+
required = false)
43+
@Nullable
44+
String fieldName;
45+
46+
@Override
47+
public TreeVisitor<?, ExecutionContext> getVisitor() {
48+
return new LogVisitor(
49+
"org.apache.commons.logging.Log",
50+
"org.apache.commons.logging.LogFactory getLog(..)",
51+
"lombok.extern.apachecommons.CommonsLog",
52+
fieldName);
53+
}
54+
55+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.lombok.log;
17+
18+
import lombok.EqualsAndHashCode;
19+
import lombok.Value;
20+
import org.jspecify.annotations.Nullable;
21+
import org.openrewrite.ExecutionContext;
22+
import org.openrewrite.Option;
23+
import org.openrewrite.TreeVisitor;
24+
25+
@Value
26+
@EqualsAndHashCode(callSuper = false)
27+
public class UseJBossLog extends UseLogRecipeTemplate {
28+
29+
@Override
30+
public String getDisplayName() {
31+
return getDisplayName("@JBossLog");
32+
}
33+
34+
@Override
35+
public String getDescription() {
36+
return getDescription("@JBossLog", "org.jboss.logging.Logger");
37+
}
38+
39+
@Option(displayName = "Name of the log field",
40+
description = FIELD_NAME_DESCRIPTION,
41+
example = "LOGGER",
42+
required = false)
43+
@Nullable
44+
String fieldName;
45+
46+
@Override
47+
public TreeVisitor<?, ExecutionContext> getVisitor() {
48+
return new LogVisitor(
49+
"org.jboss.logging.Logger",
50+
"org.jboss.logging.Logger getLogger(..)",
51+
"lombok.extern.jbosslog.JBossLog",
52+
fieldName);
53+
}
54+
55+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.lombok.log;
17+
18+
import lombok.EqualsAndHashCode;
19+
import lombok.Value;
20+
import org.jspecify.annotations.Nullable;
21+
import org.openrewrite.ExecutionContext;
22+
import org.openrewrite.Option;
23+
import org.openrewrite.TreeVisitor;
24+
25+
@Value
26+
@EqualsAndHashCode(callSuper = false)
27+
public class UseLog extends UseLogRecipeTemplate {
28+
29+
@Override
30+
public String getDisplayName() {
31+
return getDisplayName("@Log");
32+
}
33+
34+
@Override
35+
public String getDescription() {
36+
return getDescription("@Log", "java.util.logging.Logger");
37+
}
38+
39+
@Option(displayName = "Name of the log field",
40+
description = FIELD_NAME_DESCRIPTION,
41+
example = "LOGGER",
42+
required = false)
43+
@Nullable
44+
String fieldName;
45+
46+
@Override
47+
public TreeVisitor<?, ExecutionContext> getVisitor() {
48+
return new LogVisitor(
49+
"java.util.logging.Logger",
50+
"java.util.logging.Logger getLogger(String)",
51+
"lombok.extern.java.Log",
52+
fieldName) {
53+
54+
@Override
55+
protected String getFactoryParameter(String className) {
56+
return className + ".class.getName()";
57+
}
58+
};
59+
}
60+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.lombok.log;
17+
18+
import lombok.EqualsAndHashCode;
19+
import lombok.Value;
20+
import org.jspecify.annotations.Nullable;
21+
import org.openrewrite.ExecutionContext;
22+
import org.openrewrite.Option;
23+
import org.openrewrite.TreeVisitor;
24+
25+
@Value
26+
@EqualsAndHashCode(callSuper = false)
27+
public class UseLog4j2 extends UseLogRecipeTemplate {
28+
29+
@Override
30+
public String getDisplayName() {
31+
return getDisplayName("@Log4j2");
32+
}
33+
34+
@Override
35+
public String getDescription() {
36+
return getDescription("@Log4j2", "org.apache.logging.log4j.Logger");
37+
}
38+
39+
@Option(displayName = "Name of the log field",
40+
description = FIELD_NAME_DESCRIPTION,
41+
example = "LOGGER",
42+
required = false)
43+
@Nullable
44+
String fieldName;
45+
46+
@Override
47+
public TreeVisitor<?, ExecutionContext> getVisitor() {
48+
return new LogVisitor(
49+
"org.apache.logging.log4j.Logger",
50+
"org.apache.logging.log4j.LogManager getLogger(..)",
51+
"lombok.extern.log4j.Log4j2",
52+
fieldName);
53+
}
54+
55+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.lombok.log;
17+
18+
import org.openrewrite.Recipe;
19+
20+
abstract class UseLogRecipeTemplate extends Recipe {
21+
22+
protected static final String FIELD_NAME_DESCRIPTION = "Name of the log field to replace. " +
23+
"If not specified, the field name is not checked and any field that satisfies the other checks is converted.";
24+
25+
protected String getDisplayName(String annotation) {
26+
return String.format("Use `%s` instead of explicit fields", annotation);
27+
}
28+
29+
protected String getDescription(String annotation, String pathToLogger) {
30+
//language=markdown
31+
return String.format("Prefer the lombok annotation `%s` over explicitly written out `%s` fields.", annotation, pathToLogger);
32+
}
33+
34+
}

0 commit comments

Comments
 (0)