Skip to content

Commit f6bec65

Browse files
thomaszubThomas Zub
andauthored
feat: support type parameters in ImplementInterface visitor (#2194)
Co-authored-by: Thomas Zub <t.zub@lvm.de>
1 parent 0561b2c commit f6bec65

2 files changed

Lines changed: 102 additions & 4 deletions

File tree

rewrite-java-tck/src/main/kotlin/org/openrewrite/java/ImplementInterfaceTest.kt

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ import org.openrewrite.ExecutionContext
2020
import org.openrewrite.Recipe
2121
import org.openrewrite.TreeVisitor
2222
import org.openrewrite.java.tree.J
23+
import org.openrewrite.java.tree.JavaType.ShallowClass
24+
import org.openrewrite.java.tree.Space
25+
import org.openrewrite.marker.Markers
26+
import java.util.*
2327

2428
interface ImplementInterfaceTest : JavaRecipeTest {
2529
override val recipe: Recipe?
@@ -34,6 +38,41 @@ interface ImplementInterfaceTest : JavaRecipeTest {
3438
}
3539
}
3640

41+
val recipeTyped: Recipe
42+
get() = object : TestRecipe() {
43+
override fun getVisitor(): TreeVisitor<*, ExecutionContext> {
44+
return object : JavaVisitor<ExecutionContext>() {
45+
override fun visitClassDeclaration(classDecl: J.ClassDeclaration, ctx: ExecutionContext): J {
46+
doAfterVisit(
47+
ImplementInterface(
48+
classDecl,
49+
"b.B",
50+
listOf(
51+
J.Identifier(
52+
UUID.randomUUID(),
53+
Space.EMPTY,
54+
Markers.EMPTY,
55+
"String",
56+
ShallowClass.build("java.lang.String"),
57+
null
58+
),
59+
J.Identifier(
60+
UUID.randomUUID(),
61+
Space.build(" ", emptyList()),
62+
Markers.EMPTY,
63+
"LocalDate",
64+
ShallowClass.build("java.time.LocalDate"),
65+
null
66+
)
67+
)
68+
)
69+
)
70+
return classDecl
71+
}
72+
}
73+
}
74+
}
75+
3776
companion object {
3877
private const val b = "package b;\npublic interface B {}"
3978
private const val c = "package c;\npublic interface C {}"
@@ -73,4 +112,26 @@ interface ImplementInterfaceTest : JavaRecipeTest {
73112
}
74113
"""
75114
)
115+
116+
@Test
117+
fun addAnImplementedInterfaceWithTypeParameters(jp: JavaParser) = assertChanged(
118+
jp,
119+
recipe = recipeTyped,
120+
dependsOn = arrayOf(b, c),
121+
before = """
122+
import c.C;
123+
124+
class A implements C {
125+
}
126+
""",
127+
after = """
128+
import b.B;
129+
import c.C;
130+
131+
import java.time.LocalDate;
132+
133+
class A implements C, B<String, LocalDate> {
134+
}
135+
"""
136+
)
76137
}

rewrite-java/src/main/java/org/openrewrite/java/ImplementInterface.java

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,38 @@
1616
package org.openrewrite.java;
1717

1818
import org.openrewrite.internal.ListUtils;
19+
import org.openrewrite.internal.lang.Nullable;
1920
import org.openrewrite.java.tree.*;
2021
import org.openrewrite.marker.Markers;
2122

23+
import java.util.List;
24+
import java.util.Objects;
25+
import java.util.stream.Collectors;
26+
2227
import static org.openrewrite.Tree.randomId;
2328
import static org.openrewrite.java.tree.Space.format;
2429

2530
public class ImplementInterface<P> extends JavaIsoVisitor<P> {
2631
private final J.ClassDeclaration scope;
2732
private final JavaType.FullyQualified interfaceType;
33+
private final @Nullable List<Expression> typeParameters;
2834

29-
public ImplementInterface(J.ClassDeclaration scope, JavaType.FullyQualified interfaceType) {
35+
public ImplementInterface(J.ClassDeclaration scope, JavaType.FullyQualified interfaceType, @Nullable List<Expression> typeParameters) {
3036
this.scope = scope;
3137
this.interfaceType = interfaceType;
38+
this.typeParameters = typeParameters;
39+
}
40+
41+
public ImplementInterface(J.ClassDeclaration scope, String interfaze, @Nullable List<Expression> typeParameters) {
42+
this(scope, JavaType.ShallowClass.build(interfaze), typeParameters);
43+
}
44+
45+
public ImplementInterface(J.ClassDeclaration scope, JavaType.FullyQualified interfaceType) {
46+
this(scope, interfaceType, null);
3247
}
3348

3449
public ImplementInterface(J.ClassDeclaration scope, String interfaze) {
35-
this(scope, JavaType.ShallowClass.build(interfaze));
50+
this(scope, interfaze, null);
3651
}
3752

3853
@Override
@@ -41,7 +56,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, P
4156
if (c.isScope(scope) && (c.getImplements() == null || c.getImplements().stream()
4257
.noneMatch(f -> TypeUtils.isAssignableTo(f.getType(), interfaceType)))) {
4358

44-
if(!classDecl.getSimpleName().equals(interfaceType.getClassName())) {
59+
if (!classDecl.getSimpleName().equals(interfaceType.getClassName())) {
4560
maybeAddImport(interfaceType);
4661
}
4762

@@ -50,7 +65,29 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, P
5065
.withType(interfaceType)
5166
.withPrefix(format(" "));
5267

53-
c = c.withImplements(ListUtils.concat(c.getImplements(), impl));
68+
if (typeParameters != null && !typeParameters.isEmpty()) {
69+
typeParameters.stream()
70+
.map(Expression::getType)
71+
.map(t -> (t instanceof JavaType.FullyQualified) ? (JavaType.FullyQualified) t : null)
72+
.filter(Objects::nonNull)
73+
.forEach(t -> maybeAddImport(t.getFullyQualifiedName()));
74+
75+
List<JRightPadded<Expression>> elements = typeParameters.stream()
76+
.map(t -> new JRightPadded<>(t, Space.EMPTY, Markers.EMPTY))
77+
.collect(Collectors.toList());
78+
79+
J.ParameterizedType typedImpl = new J.ParameterizedType(
80+
randomId(),
81+
Space.EMPTY,
82+
Markers.EMPTY,
83+
impl,
84+
JContainer.build(Space.EMPTY, elements, Markers.EMPTY)
85+
);
86+
87+
c = c.withImplements(ListUtils.concat(c.getImplements(), typedImpl));
88+
} else {
89+
c = c.withImplements(ListUtils.concat(c.getImplements(), impl));
90+
}
5491

5592
JContainer<TypeTree> anImplements = c.getPadding().getImplements();
5693
assert anImplements != null;

0 commit comments

Comments
 (0)