Skip to content

Commit

Permalink
Merge branch 'main' into jakarta-ee-recipe-cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
timtebeek authored Apr 20, 2024
2 parents 067a450 + 3f4b6ae commit b895102
Show file tree
Hide file tree
Showing 8 changed files with 725 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.TypeUtils;

import static org.openrewrite.java.tree.J.ClassDeclaration.Kind.Type.Interface;

@Value
@EqualsAndHashCode(callSuper = false)
public class AddMissingMethodImplementation extends Recipe {

@Option(displayName = "Fully Qualified Class Name",
description = "A fully qualified class being implemented with missing method.",
example = "com.yourorg.FooBar")
Expand All @@ -49,7 +52,7 @@ public class AddMissingMethodImplementation extends Recipe {

@Override
public String getDisplayName() {
return "Adds missing method implementations.";
return "Adds missing method implementations";
}

@Override
Expand All @@ -63,6 +66,7 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
}

public class ClassImplementationVisitor extends JavaIsoVisitor<ExecutionContext> {

private final JavaTemplate methodTemplate = JavaTemplate.builder(methodTemplateString).build();
private final MethodMatcher methodMatcher = new MethodMatcher(methodPattern, true);

Expand All @@ -71,8 +75,8 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration cs, Execution
// need to make sure we handle sub-classes
J.ClassDeclaration classDecl = super.visitClassDeclaration(cs, ctx);

// No need to make changes to abstract classes; only change concrete classes.
if (classDecl.hasModifier(J.Modifier.Type.Abstract)) {
// No need to make changes to abstract classes or interfaces; only change concrete classes.
if (classDecl.hasModifier(J.Modifier.Type.Abstract) || classDecl.getKind() == Interface) {
return classDecl;
}
// Don't make changes to classes that don't match the fully qualified name
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.javax;

import lombok.EqualsAndHashCode;
import org.openrewrite.*;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.search.FindAnnotations;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.tree.J;

import java.util.Comparator;

@EqualsAndHashCode(callSuper = false)
public class AddDefaultConstructorToEntityClass extends Recipe {
@Override
public String getDisplayName() {
return "`@Entity` objects with constructors must also have a default constructor";
}

@Override
public String getDescription() {
return "When a Java Persistence API (JPA) entity class has a constructor with arguments, the class must also " +
"have a default, no-argument constructor. The OpenJPA implementation automatically generates the " +
"no-argument constructor, but the EclipseLink implementation does not.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(
Preconditions.or(
new UsesType<>("javax.persistence.Entity", true),
new UsesType<>("javax.persistence.MappedSuperclass", true)),
new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
// Exit if class not annotated with either @Entity or @MappedSuperclass
if (FindAnnotations.find(classDecl, "javax.persistence.Entity").isEmpty()
&& FindAnnotations.find(classDecl, "javax.persistence.MappedSuperclass").isEmpty()) {
return classDecl;
}

// Exit if class already has default no-arg constructor
if (classDecl.getBody().getStatements().stream()
.filter(statement -> statement instanceof J.MethodDeclaration)
.map(J.MethodDeclaration.class::cast)
.filter(J.MethodDeclaration::isConstructor)
.anyMatch(constructor -> constructor.getParameters().get(0) instanceof J.Empty)) {
return classDecl;
}

// Add default constructor with empty body
return classDecl.withBody(JavaTemplate.builder("public #{}(){}")
.contextSensitive()
.build()
.apply(new Cursor(getCursor(), classDecl.getBody()),
classDecl.getBody().getCoordinates().firstStatement(),
classDecl.getSimpleName()
)
);
}
}
);
}
}
2 changes: 1 addition & 1 deletion src/main/resources/META-INF/rewrite/java-version-7.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ recipeList:
methodTemplateString: "public java.lang.String getSchema() { \n\t// TODO Auto-generated method stub\n return null; }"
- org.openrewrite.java.migrate.AddMissingMethodImplementation:
fullyQualifiedClassName: java.sql.Connection
methodPattern: "*..* setNetworkTimeout(java.util.concurrent.Executor)"
methodPattern: "*..* setNetworkTimeout(java.util.concurrent.Executor, int)"
methodTemplateString: "public void setNetworkTimeout(java.util.concurrent.Executor executor, int milliseconds) { \n\t// TODO Auto-generated method stub\n }"
- org.openrewrite.java.migrate.AddMissingMethodImplementation:
fullyQualifiedClassName: java.sql.Connection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ tags:
recipeList:
- org.openrewrite.java.migrate.javax.AddTableGenerator
- org.openrewrite.java.migrate.javax.AddColumnAnnotation
- org.openrewrite.java.migrate.javax.RemoveEmbeddableId
- org.openrewrite.java.migrate.javax.AddDefaultConstructorToEntityClass
- org.openrewrite.java.migrate.javax.RemoveEmbeddableId
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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;

import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.Issue;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;
import static org.openrewrite.java.Assertions.javaVersion;

class AddMissingMethodImplementationTest implements RewriteTest {

@Override
public void defaults(RecipeSpec spec) {
spec.recipe(
new AddMissingMethodImplementation("I1", "*..* m1()",
"public void m1() { System.out.println(\"m1\"); }"))
.allSources(src -> src.markers(javaVersion(21)));
}

@Test
@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/459")
void skipInterfaces() {
//language=java
rewriteRun(
java(
"""
interface I1 {}
interface I2 extends I1 {}
"""
)
);
}

@Test
void skipAbstractClasses() {
//language=java
rewriteRun(
java(
"""
interface I1 {}
abstract class AC2 implements I1 {}
"""
)
);
}

@DocumentExample
@Test
void happyPath() {
//language=java
rewriteRun(
java(
"""
interface I1 {}
class C2 implements I1 {}
""",
"""
interface I1 {}
class C2 implements I1 {
public void m1() {
System.out.println("m1");
}}
"""
)
);
}

@Test
void methodExists() {
//language=java
rewriteRun(
java(
"""
interface I1 {}
class C2 implements I1 {
public void m1() {
System.out.println("m1");
}
}
"""
)
);
}

@Test
void methodExistsDiffImpl() {
//language=java
rewriteRun(
java(
"""
interface I1 {}
class C2 implements I1 {
public void m1() {
System.out.println("m1 diff");
}
}
"""
)
);
}

@Test
void methodExistsDiffSignature() {
//language=java
rewriteRun(
java(
"""
interface I1 {}
class C2 implements I1 {
protected void m1() {
System.out.println("m1");
}
}
"""
)
);
}

}
Loading

0 comments on commit b895102

Please sign in to comment.