From 76e56920471d619d57e1c18c802716a483149797 Mon Sep 17 00:00:00 2001 From: Marcel Reiter <131314419+marcel-gepardec@users.noreply.github.com> Date: Fri, 11 Oct 2024 22:25:40 +0200 Subject: [PATCH] added the option to add, replace or remove arrays in annotation attributes (#4446) Co-authored-by: marcel-gepardec Co-authored-by: Tim te Beek --- .../AddOrUpdateAnnotationAttributeTest.java | 176 ++++++++++++++++++ .../java/AddOrUpdateAnnotationAttribute.java | 61 +++++- 2 files changed, 232 insertions(+), 5 deletions(-) diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/AddOrUpdateAnnotationAttributeTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/AddOrUpdateAnnotationAttributeTest.java index a04d9fc4594..259197e15ab 100755 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/AddOrUpdateAnnotationAttributeTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/AddOrUpdateAnnotationAttributeTest.java @@ -492,4 +492,180 @@ void foo() { ) ); } + + @Test + void arrayInAnnotationAttribute() { + rewriteRun( + spec -> spec.recipe(new AddOrUpdateAnnotationAttribute( + "org.example.Foo", + "array", + "newTest", + false)), + java( + """ + package org.example; + public @interface Foo { + String[] array() default {}; + } + """ + ), + java( + """ + import org.example.Foo; + + @Foo(array = {"oldTest"}) + public class A { + } + """, + """ + import org.example.Foo; + + @Foo(array = {"newTest"}) + public class A { + } + """ + ) + ); + } + + @Test + void arrayInputMoreThanOneInAnnotationAttribute() { + rewriteRun( + spec -> spec.recipe(new AddOrUpdateAnnotationAttribute( + "org.example.Foo", + "array", + "newTest1,newTest2", + false)), + java( + """ + package org.example; + public @interface Foo { + String[] array() default {}; + } + """ + ), + java( + """ + import org.example.Foo; + + @Foo(array = {"oldTest"}) + public class A { + } + """, + """ + import org.example.Foo; + + @Foo(array = {"newTest1", "newTest2"}) + public class A { + } + """ + ) + ); + } + + @Test + void addArrayInputInAnnotationAttribute() { + rewriteRun( + spec -> spec.recipe(new AddOrUpdateAnnotationAttribute( + "org.example.Foo", + "array", + "newTest1,newTest2", + false)), + java( + """ + package org.example; + public @interface Foo { + String[] array() default {}; + } + """ + ), + java( + """ + import org.example.Foo; + + @Foo + public class A { + } + """, + """ + import org.example.Foo; + + @Foo(array = {"newTest1", "newTest2"}) + public class A { + } + """ + ) + ); + } + + @Test + void removeArrayInputInAnnotationAttribute() { + rewriteRun( + spec -> spec.recipe(new AddOrUpdateAnnotationAttribute( + "org.example.Foo", + "array", + null, + null)), + java( + """ + package org.example; + public @interface Foo { + String[] array() default {}; + } + """ + ), + java( + """ + import org.example.Foo; + + @Foo(array = {"newTest1", "newTest2"}) + public class A { + } + """, + """ + import org.example.Foo; + + @Foo + public class A { + } + """ + ) + ); + } + + @Test + void addOtherAttributeInArrayAnnotation() { + rewriteRun( + spec -> spec.recipe(new AddOrUpdateAnnotationAttribute( + "org.example.Foo", + "string", + "test", + null)), + java( + """ + package org.example; + public @interface Foo { + String[] array() default {}; + String string() default ""; + } + """ + ), + java( + """ + import org.example.Foo; + + @Foo(array = {"newTest1", "newTest2"}) + public class A { + } + """, + """ + import org.example.Foo; + + @Foo(string = "test", array = {"newTest1", "newTest2"}) + public class A { + } + """ + ) + ); + } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/AddOrUpdateAnnotationAttribute.java b/rewrite-java/src/main/java/org/openrewrite/java/AddOrUpdateAnnotationAttribute.java index b871f126b3d..8ed9e8f77db 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/AddOrUpdateAnnotationAttribute.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/AddOrUpdateAnnotationAttribute.java @@ -25,9 +25,13 @@ import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.TypeUtils; +import org.openrewrite.marker.Markers; +import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; @Value @EqualsAndHashCode(callSuper = false) @@ -88,10 +92,18 @@ public J.Annotation visitAnnotation(J.Annotation a, ExecutionContext ctx) { .build() .apply(getCursor(), a.getCoordinates().replaceArguments(), newAttributeValue); } else { + String newAttributeValueResult = newAttributeValue; + if (((JavaType.FullyQualified) Objects.requireNonNull(a.getAnnotationType().getType())).getMethods().stream().anyMatch(method -> method.getReturnType().toString().equals("java.lang.String[]"))) { + String attributeValueCleanedUp = attributeValue.replaceAll("\\s+","").replaceAll("[\\s+{}\"]",""); + List attributeList = Arrays.asList(attributeValueCleanedUp.contains(",") ? attributeValueCleanedUp.split(",") : new String[]{attributeValueCleanedUp}); + newAttributeValueResult = attributeList.stream() + .map(String::valueOf) + .collect(Collectors.joining("\", \"", "{\"", "\"}")); + } return JavaTemplate.builder("#{} = #{}") .contextSensitive() .build() - .apply(getCursor(), a.getCoordinates().replaceArguments(), attributeName, newAttributeValue); + .apply(getCursor(), a.getCoordinates().replaceArguments(), attributeName, newAttributeValueResult); } } else { // First assume the value exists amongst the arguments and attempt to update it @@ -105,14 +117,52 @@ public J.Annotation visitAnnotation(J.Annotation a, ExecutionContext ctx) { return it; } foundOrSetAttributeWithDesiredValue.set(true); - J.Literal value = (J.Literal) as.getAssignment(); + if (newAttributeValue == null) { return null; } - if (newAttributeValue.equals(value.getValueSource()) || Boolean.TRUE.equals(addOnly)) { - return it; + + if (as.getAssignment() instanceof J.NewArray){ + List jLiteralList = ((J.NewArray) as.getAssignment()).getInitializer(); + String attributeValueCleanedUp = attributeValue.replaceAll("\\s+","").replaceAll("[\\s+{}\"]",""); + List attributeList = Arrays.asList(attributeValueCleanedUp.contains(",") ? attributeValueCleanedUp.split(",") : new String[]{attributeValueCleanedUp}); + int m = 0; + for (int i = 0; i< Objects.requireNonNull(jLiteralList).size(); i++){ + if (i >= attributeList.size()){ + jLiteralList.remove(i); + i--; + continue; + } + + String newAttributeListValue = maybeQuoteStringArgument(attributeName, attributeList.get(i), finalA); + if (jLiteralList.size() == i+1){ + m = i+1; + } + + if (newAttributeListValue != null && newAttributeListValue.equals(((J.Literal) jLiteralList.get(i)).getValueSource()) || Boolean.TRUE.equals(addOnly)) { + continue; + } + + jLiteralList.set(i, ((J.Literal) jLiteralList.get(i)).withValue(newAttributeListValue).withValueSource(newAttributeListValue).withPrefix(jLiteralList.get(i).getPrefix())); + } + if (jLiteralList.size() < attributeList.size() || Boolean.TRUE.equals(addOnly)){ + if (Boolean.TRUE.equals(addOnly)){ + m = 0; + } + for (int j = m; j < attributeList.size(); j++){ + String newAttributeListValue = maybeQuoteStringArgument(attributeName, attributeList.get(j), finalA); + jLiteralList.add(j, new J.Literal(Tree.randomId(), jLiteralList.get(j-1).getPrefix(), Markers.EMPTY, newAttributeListValue, newAttributeListValue, null, JavaType.Primitive.String)); + } + } + + return as.withAssignment(((J.NewArray) as.getAssignment()).withInitializer(jLiteralList)); + } else { + J.Literal value = (J.Literal) as.getAssignment(); + if (newAttributeValue.equals(value.getValueSource()) || Boolean.TRUE.equals(addOnly)) { + return it; + } + return as.withAssignment(value.withValue(newAttributeValue).withValueSource(newAttributeValue)); } - return as.withAssignment(value.withValue(newAttributeValue).withValueSource(newAttributeValue)); } else if (it instanceof J.Literal) { // The only way anything except an assignment can appear is if there's an implicit assignment to "value" if (attributeName == null || "value".equals(attributeName)) { @@ -164,6 +214,7 @@ public J.Annotation visitAnnotation(J.Annotation a, ExecutionContext ctx) { a = a.withArguments(newArgs); } if (foundOrSetAttributeWithDesiredValue.get()) { + a = autoFormat(a, ctx); return a; } // There was no existing value to update, so add a new value into the argument list