Skip to content

Commit b3cd1bc

Browse files
authored
Annotation attribute non jvm class misses import (#6786)
* Annotation attribute non jvm class misses import * Correct test
1 parent f70aad1 commit b3cd1bc

2 files changed

Lines changed: 109 additions & 15 deletions

File tree

rewrite-java-test/src/test/java/org/openrewrite/java/AddOrUpdateAnnotationAttributeTest.java

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2901,6 +2901,95 @@ public class A {}
29012901
);
29022902
}
29032903

2904+
@Test
2905+
void appendClassValueToExistingNamedClassAttributeWithImport() {
2906+
rewriteRun(
2907+
spec -> spec.recipe(new AddOrUpdateAnnotationAttribute(
2908+
"org.example.Foo",
2909+
"classes",
2910+
"org.example.data.SomeData.class",
2911+
null,
2912+
null,
2913+
true
2914+
)),
2915+
java(
2916+
"""
2917+
package org.example;
2918+
public @interface Foo {
2919+
Class<?>[] classes();
2920+
}
2921+
"""
2922+
),
2923+
java(
2924+
"""
2925+
package org.example.data;
2926+
public interface SomeData {
2927+
}
2928+
"""
2929+
),
2930+
java(
2931+
"""
2932+
import org.example.Foo;
2933+
2934+
@Foo(classes = Integer.class)
2935+
public class A {}
2936+
""",
2937+
"""
2938+
import org.example.Foo;
2939+
import org.example.data.SomeData;
2940+
2941+
@Foo(classes = {Integer.class, SomeData.class})
2942+
public class A {}
2943+
"""
2944+
)
2945+
);
2946+
}
2947+
2948+
@Test
2949+
void createClassValueAttributeWithImportNonJvmType() {
2950+
rewriteRun(
2951+
spec -> spec.recipe(new AddOrUpdateAnnotationAttribute(
2952+
"org.example.Foo",
2953+
"classes",
2954+
"org.example.data.SomeData.class",
2955+
null,
2956+
null,
2957+
true
2958+
)),
2959+
java(
2960+
"""
2961+
package org.example;
2962+
public @interface Foo {
2963+
Class<?>[] classes();
2964+
String name;
2965+
}
2966+
"""
2967+
),
2968+
java(
2969+
"""
2970+
package org.example.data;
2971+
public interface SomeData {
2972+
}
2973+
"""
2974+
),
2975+
java(
2976+
"""
2977+
import org.example.Foo;
2978+
2979+
@Foo(name = "name")
2980+
public class A {}
2981+
""",
2982+
"""
2983+
import org.example.Foo;
2984+
import org.example.data.SomeData;
2985+
2986+
@Foo(classes = SomeData.class, name = "name")
2987+
public class A {}
2988+
"""
2989+
)
2990+
);
2991+
}
2992+
29042993
@Test
29052994
void dottedStringValueIsNotTreatedAsEnum() {
29062995
rewriteRun(

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

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,10 @@ public J.Annotation visitAnnotation(J.Annotation original, ExecutionContext ctx)
116116
}
117117
if ("value".equals(attributeName())) {
118118
String attrVal = newAttributeValue.contains(",") && attributeIsArray(a) ? getAttributeValuesAsArray(a) : newAttributeValue;
119-
return JavaTemplate.apply("#{}", getCursor(), a.getCoordinates().replaceArguments(), attrVal);
119+
return getJavaTemplate("#{}").apply(getCursor(), a.getCoordinates().replaceArguments(), attrVal);
120120
}
121121
String attrVal = newAttributeValue.contains(",") && attributeIsArray(a) ? getAttributeValuesAsString(a) : newAttributeValue;
122-
return JavaTemplate.apply("#{} = #{}", getCursor(), a.getCoordinates().replaceArguments(), attributeName, attrVal);
122+
return getJavaTemplate("#{} = #{}").apply(getCursor(), a.getCoordinates().replaceArguments(), attributeName, attrVal);
123123
}
124124

125125
// UPDATE the value when the annotation has arguments, e.g. @Foo(name="old") to `@Foo(name="new")
@@ -199,7 +199,7 @@ public J.Annotation visitAnnotation(J.Annotation original, ExecutionContext ctx)
199199
return as.withAssignment(createNewArrayWithExistingAndNew(annotation, (J.FieldAccess) exp, getAttributeValues(annotation)));
200200
}
201201
//noinspection ConstantConditions
202-
return JavaTemplate.<J.Annotation>apply("#{} = #{}", getCursor(), as.getCoordinates().replace(), var_.getSimpleName(), newAttributeValue)
202+
return getJavaTemplate("#{} = #{}").<J.Annotation>apply(getCursor(), as.getCoordinates().replace(), var_.getSimpleName(), newAttributeValue)
203203
.getArguments().get(annotation.getArguments().indexOf(as));
204204
}
205205
return as;
@@ -248,13 +248,24 @@ public J.Annotation visitAnnotation(J.Annotation original, ExecutionContext ctx)
248248
getAttributeValues(annotation).stream().map(String::valueOf).collect(joining(",", "{", "}")) :
249249
newAttributeValue;
250250
//noinspection ConstantConditions
251-
return JavaTemplate.<J.Annotation>apply("#{}", getCursor(), annotation.getCoordinates().replaceArguments(), attrVal)
251+
return getJavaTemplate("#{}").<J.Annotation>apply(getCursor(), annotation.getCoordinates().replaceArguments(), attrVal)
252252
.getArguments().get(0);
253253
}
254254
// Make the attribute name explicit, before we add the new value below
255255
return createAnnotationAssignment(annotation, "value", fieldAccess);
256256
}
257257

258+
private JavaTemplate getJavaTemplate(String template) {
259+
JavaTemplate.Builder builder = JavaTemplate.builder(template);
260+
if (isFullyQualifiedClass()) {
261+
JavaType.ShallowClass fqn = JavaType.ShallowClass.build(attributeValue.substring(0, attributeValue.length() - 6));
262+
builder
263+
.javaParser(JavaParser.fromJavaVersion().dependsOn(String.format("package %s;\npublic interface %s {}\n", fqn.getPackageName(), fqn.getClassName())))
264+
.imports(fqn.getFullyQualifiedName());
265+
}
266+
return builder.build();
267+
}
268+
258269
private @Nullable Expression update(J.NewArray arrayValue, J.Annotation annotation, @Nullable String newAttributeValue) {
259270
if (newAttributeValue == null) {
260271
return null;
@@ -268,13 +279,13 @@ public J.Annotation visitAnnotation(J.Annotation original, ExecutionContext ctx)
268279
private Expression createAnnotationLiteral(J.Annotation annotation, String newAttributeValue) {
269280
String attrVal = newAttributeValue.contains(",") && attributeIsArray(annotation) ? getAttributeValuesAsString(annotation) : newAttributeValue;
270281
//noinspection ConstantConditions
271-
return JavaTemplate.<J.Annotation>apply("#{}", getCursor(), annotation.getCoordinates().replaceArguments(), attrVal)
282+
return getJavaTemplate("#{}").<J.Annotation>apply(getCursor(), annotation.getCoordinates().replaceArguments(), attrVal)
272283
.getArguments().get(0);
273284
}
274285

275286
private J.Assignment createAnnotationAssignment(J.Annotation annotation, String name, @Nullable Object parameter) {
276287
//noinspection ConstantConditions
277-
return (J.Assignment) JavaTemplate.<J.Annotation>apply(name + " = " + (parameter instanceof J ? "#{any()}" : "#{}"), getCursor(), annotation.getCoordinates().replaceArguments(), parameter)
288+
return (J.Assignment) getJavaTemplate(name + " = " + (parameter instanceof J ? "#{any()}" : "#{}")).<J.Annotation>apply(getCursor(), annotation.getCoordinates().replaceArguments(), parameter)
278289
.getArguments().get(0);
279290
}
280291

@@ -290,7 +301,7 @@ private J.NewArray createNewArrayWithExistingAndNew(J.Annotation annotation, J.L
290301
}
291302
// Use a template to create the array structure, then replace the initializer
292303
//noinspection ConstantConditions
293-
J.NewArray template = (J.NewArray) JavaTemplate.<J.Annotation>apply("{#{any()}}", getCursor(), annotation.getCoordinates().replaceArguments(), existingLiteral)
304+
J.NewArray template = (J.NewArray) getJavaTemplate("{#{any()}}").<J.Annotation>apply(getCursor(), annotation.getCoordinates().replaceArguments(), existingLiteral)
294305
.getArguments().get(0);
295306
return template.withInitializer(initializer);
296307
}
@@ -302,19 +313,13 @@ private J.NewArray createNewArrayWithExistingAndNew(J.Annotation annotation, J.F
302313
// Add new values, skipping duplicates - use template for non-string values
303314
for (String attribute : newValues) {
304315
if (!attributeNameOrValIsAlreadyPresent(initializer, singleton(attribute))) {
305-
JavaTemplate.Builder templateBuilder = JavaTemplate.builder("#{}");
306316
String templateValue = attribute;
307317
// For FQ classes, add stub so type info is resolved and use FQ name in template
308318
if (isFullyQualifiedClass()) {
309-
String fqClassName = attributeValue.substring(0, attributeValue.length() - 6);
310-
String packageName = fqClassName.substring(0, fqClassName.lastIndexOf('.'));
311-
String simpleName = fqClassName.substring(fqClassName.lastIndexOf('.') + 1);
312-
templateBuilder = templateBuilder.javaParser(JavaParser.fromJavaVersion()
313-
.dependsOn("package " + packageName + "; public class " + simpleName + " {}"));
314-
templateValue = fqClassName + ".class";
319+
templateValue = getFullyQualifiedClass(attributeValue);
315320
}
316321
//noinspection ConstantConditions
317-
Expression newExpr = templateBuilder.build().<J.Annotation>apply(getCursor(), annotation.getCoordinates().replaceArguments(), templateValue)
322+
Expression newExpr = getJavaTemplate("#{}").<J.Annotation>apply(getCursor(), annotation.getCoordinates().replaceArguments(), templateValue)
318323
.getArguments().get(0).withPrefix(SINGLE_SPACE);
319324
initializer.add(newExpr);
320325
}

0 commit comments

Comments
 (0)