Skip to content

Commit

Permalink
Added support for annotations in fully qualified type names. (#3818)
Browse files Browse the repository at this point in the history
* Added support for annotations in fully qualified type names.

* Add `J.AnnotatedType#getAllAnnotations()`

Also removed some code from `J.MethodDeclaration#getAllAnnotations()` and `J.VariableDeclarations#getAllAnnotations()` as that returned type annotations rather than annotations on the variable / method.

---------

Co-authored-by: Knut Wannheden <knut@moderne.io>
  • Loading branch information
traceyyoshima and knutwannheden authored Dec 14, 2023
1 parent eb423e5 commit cba6919
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1237,11 +1237,51 @@ public J visitTypeCast(TypeCastTree node, Space fmt) {
convert(node.getType(), t -> sourceBefore(")"))),
convert(node.getExpression()));
}

@Override
public J visitAnnotatedType(AnnotatedTypeTree node, Space fmt) {
return new J.AnnotatedType(randomId(), fmt, Markers.EMPTY, convertAll(node.getAnnotations()),
convert(node.getUnderlyingType()));
Map<Integer, JCAnnotation> annotationPosTable = new HashMap<>();
for (AnnotationTree annotationNode : node.getAnnotations()) {
JCAnnotation annotation = (JCAnnotation) annotationNode;
annotationPosTable.put(annotation.pos, annotation);
}
return new J.AnnotatedType(randomId(), fmt, Markers.EMPTY, leadingAnnotations(annotationPosTable),
annotationPosTable.isEmpty() ? convert(node.getUnderlyingType()) : annotatedTypeTree(node.getUnderlyingType(), annotationPosTable));
}

private List<J.Annotation> leadingAnnotations(Map<Integer, JCAnnotation> annotationPosTable) {
List<J.Annotation> annotations = new ArrayList<>(annotationPosTable.size());
int saveCursor = cursor;
whitespace();
while (annotationPosTable.containsKey(cursor)) {
JCTree.JCAnnotation jcAnnotation = annotationPosTable.get(cursor);
annotationPosTable.remove(cursor);
cursor = saveCursor;
J.Annotation ann = convert(jcAnnotation);
annotations.add(ann);
saveCursor = cursor;
whitespace();
}
cursor = saveCursor;
return annotations.isEmpty() ? emptyList() : annotations;
}

private TypeTree annotatedTypeTree(Tree node, Map<Integer, JCAnnotation> annotationPosTable) {
Space prefix = whitespace();
if (node instanceof JCFieldAccess) {
JCFieldAccess fieldAccess = (JCFieldAccess) node;
JavaType type = typeMapping.type(node);
Expression select = (Expression) annotatedTypeTree(fieldAccess.selected, annotationPosTable);
Space dotPrefix = sourceBefore(".");
List<J.Annotation> annotations = leadingAnnotations(annotationPosTable);
return new J.FieldAccess(randomId(), prefix, Markers.EMPTY,
select,
padLeft(dotPrefix, new J.Identifier(randomId(),
sourceBefore(fieldAccess.name.toString()), Markers.EMPTY,
annotations, fieldAccess.name.toString(), type, typeMapping.variableType(fieldAccess.sym))),
type
);
}
return convert(node);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1315,8 +1315,49 @@ public J visitTypeCast(TypeCastTree node, Space fmt) {

@Override
public J visitAnnotatedType(AnnotatedTypeTree node, Space fmt) {
return new J.AnnotatedType(randomId(), fmt, Markers.EMPTY, convertAll(node.getAnnotations()),
convert(node.getUnderlyingType()));
Map<Integer, JCAnnotation> annotationPosTable = new HashMap<>();
for (AnnotationTree annotationNode : node.getAnnotations()) {
JCAnnotation annotation = (JCAnnotation) annotationNode;
annotationPosTable.put(annotation.pos, annotation);
}
return new J.AnnotatedType(randomId(), fmt, Markers.EMPTY, leadingAnnotations(annotationPosTable),
annotationPosTable.isEmpty() ? convert(node.getUnderlyingType()) : annotatedTypeTree(node.getUnderlyingType(), annotationPosTable));
}

private List<J.Annotation> leadingAnnotations(Map<Integer, JCAnnotation> annotationPosTable) {
List<J.Annotation> annotations = new ArrayList<>(annotationPosTable.size());
int saveCursor = cursor;
whitespace();
while (annotationPosTable.containsKey(cursor)) {
JCTree.JCAnnotation jcAnnotation = annotationPosTable.get(cursor);
annotationPosTable.remove(cursor);
cursor = saveCursor;
J.Annotation ann = convert(jcAnnotation);
annotations.add(ann);
saveCursor = cursor;
whitespace();
}
cursor = saveCursor;
return annotations.isEmpty() ? emptyList() : annotations;
}

private TypeTree annotatedTypeTree(Tree node, Map<Integer, JCAnnotation> annotationPosTable) {
Space prefix = whitespace();
if (node instanceof JCFieldAccess) {
JCFieldAccess fieldAccess = (JCFieldAccess) node;
JavaType type = typeMapping.type(node);
Expression select = (Expression) annotatedTypeTree(fieldAccess.selected, annotationPosTable);
Space dotPrefix = sourceBefore(".");
List<J.Annotation> annotations = leadingAnnotations(annotationPosTable);
return new J.FieldAccess(randomId(), prefix, Markers.EMPTY,
select,
padLeft(dotPrefix, new J.Identifier(randomId(),
sourceBefore(fieldAccess.name.toString()), Markers.EMPTY,
annotations, fieldAccess.name.toString(), type, typeMapping.variableType(fieldAccess.sym))),
type
);
}
return convert(node);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1314,9 +1314,49 @@ public J visitTypeCast(TypeCastTree node, Space fmt) {
}

@Override
public J visitAnnotatedType(AnnotatedTypeTree node, Space fmt) {
return new J.AnnotatedType(randomId(), fmt, Markers.EMPTY, convertAll(node.getAnnotations()),
convert(node.getUnderlyingType()));
public J visitAnnotatedType(AnnotatedTypeTree node, Space fmt) {Map<Integer, JCAnnotation> annotationPosTable = new HashMap<>();
for (AnnotationTree annotationNode : node.getAnnotations()) {
JCAnnotation annotation = (JCAnnotation) annotationNode;
annotationPosTable.put(annotation.pos, annotation);
}
return new J.AnnotatedType(randomId(), fmt, Markers.EMPTY, leadingAnnotations(annotationPosTable),
annotationPosTable.isEmpty() ? convert(node.getUnderlyingType()) : annotatedTypeTree(node.getUnderlyingType(), annotationPosTable));
}

private List<J.Annotation> leadingAnnotations(Map<Integer, JCAnnotation> annotationPosTable) {
List<J.Annotation> annotations = new ArrayList<>(annotationPosTable.size());
int saveCursor = cursor;
whitespace();
while (annotationPosTable.containsKey(cursor)) {
JCTree.JCAnnotation jcAnnotation = annotationPosTable.get(cursor);
annotationPosTable.remove(cursor);
cursor = saveCursor;
J.Annotation ann = convert(jcAnnotation);
annotations.add(ann);
saveCursor = cursor;
whitespace();
}
cursor = saveCursor;
return annotations.isEmpty() ? emptyList() : annotations;
}

private TypeTree annotatedTypeTree(Tree node, Map<Integer, JCAnnotation> annotationPosTable) {
Space prefix = whitespace();
if (node instanceof JCFieldAccess) {
JCFieldAccess fieldAccess = (JCFieldAccess) node;
JavaType type = typeMapping.type(node);
Expression select = (Expression) annotatedTypeTree(fieldAccess.selected, annotationPosTable);
Space dotPrefix = sourceBefore(".");
List<J.Annotation> annotations = leadingAnnotations(annotationPosTable);
return new J.FieldAccess(randomId(), prefix, Markers.EMPTY,
select,
padLeft(dotPrefix, new J.Identifier(randomId(),
sourceBefore(fieldAccess.name.toString()), Markers.EMPTY,
annotations, fieldAccess.name.toString(), type, typeMapping.variableType(fieldAccess.sym))),
type
);
}
return convert(node);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1231,8 +1231,49 @@ public J visitTypeCast(TypeCastTree node, Space fmt) {

@Override
public J visitAnnotatedType(AnnotatedTypeTree node, Space fmt) {
return new J.AnnotatedType(randomId(), fmt, Markers.EMPTY, convertAll(node.getAnnotations()),
convert(node.getUnderlyingType()));
Map<Integer, JCAnnotation> annotationPosTable = new HashMap<>();
for (AnnotationTree annotationNode : node.getAnnotations()) {
JCAnnotation annotation = (JCAnnotation) annotationNode;
annotationPosTable.put(annotation.pos, annotation);
}
return new J.AnnotatedType(randomId(), fmt, Markers.EMPTY, leadingAnnotations(annotationPosTable),
annotationPosTable.isEmpty() ? convert(node.getUnderlyingType()) : annotatedTypeTree(node.getUnderlyingType(), annotationPosTable));
}

private List<J.Annotation> leadingAnnotations(Map<Integer, JCAnnotation> annotationPosTable) {
List<J.Annotation> annotations = new ArrayList<>(annotationPosTable.size());
int saveCursor = cursor;
whitespace();
while (annotationPosTable.containsKey(cursor)) {
JCTree.JCAnnotation jcAnnotation = annotationPosTable.get(cursor);
annotationPosTable.remove(cursor);
cursor = saveCursor;
J.Annotation ann = convert(jcAnnotation);
annotations.add(ann);
saveCursor = cursor;
whitespace();
}
cursor = saveCursor;
return annotations.isEmpty() ? emptyList() : annotations;
}

private TypeTree annotatedTypeTree(Tree node, Map<Integer, JCAnnotation> annotationPosTable) {
Space prefix = whitespace();
if (node instanceof JCFieldAccess) {
JCFieldAccess fieldAccess = (JCFieldAccess) node;
JavaType type = typeMapping.type(node);
Expression select = (Expression) annotatedTypeTree(fieldAccess.selected, annotationPosTable);
Space dotPrefix = sourceBefore(".");
List<J.Annotation> annotations = leadingAnnotations(annotationPosTable);
return new J.FieldAccess(randomId(), prefix, Markers.EMPTY,
select,
padLeft(dotPrefix, new J.Identifier(randomId(),
sourceBefore(fieldAccess.name.toString()), Markers.EMPTY,
annotations, fieldAccess.name.toString(), type, typeMapping.variableType(fieldAccess.sym))),
type
);
}
return convert(node);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,66 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ob
}.visit(cu, 0))
)
);
}

@Issue("https://github.com/openrewrite/rewrite/issues/3683")
@Test
void annotationAfterVariableTypePackageName() {
rewriteRun(
java(
"""
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;
import static java.lang.annotation.ElementType.*;
public class A {
@Leading java. util. @Multi1 @Multi2 List<String> l;
@Leading java. util. @Multi1 @Multi2 List<String> m() { return null; }
}
@Retention(RetentionPolicy.RUNTIME)
@Target(value={FIELD, METHOD})
public @interface Leading {}
@Retention(RetentionPolicy.RUNTIME)
@Target(value=TYPE_USE)
public @interface Multi1 {}
@Retention(RetentionPolicy.RUNTIME)
@Target(value=TYPE_USE)
public @interface Multi2 {}
""",
spec -> spec.afterRecipe(cu -> {
J.VariableDeclarations field = (J.VariableDeclarations) cu.getClasses().get(0).getBody().getStatements().get(0);
assertThat(field.getAllAnnotations()).satisfiesExactly(
leading -> assertThat(leading.getSimpleName()).isEqualTo("Leading")
);
J.ParameterizedType fieldType = (J.ParameterizedType) field.getTypeExpression();
assertThat(fieldType).isNotNull();
J.AnnotatedType annotatedType = (J.AnnotatedType) fieldType.getClazz();
assertThat(annotatedType.getAllAnnotations()).satisfiesExactly(
multi1 -> assertThat(multi1.getSimpleName()).isEqualTo("Multi1"),
multi2 -> assertThat(multi2.getSimpleName()).isEqualTo("Multi2")
);

J.MethodDeclaration method = (J.MethodDeclaration) cu.getClasses().get(0).getBody().getStatements().get(1);
assertThat(method.getAllAnnotations()).satisfiesExactly(
leading -> assertThat(leading.getSimpleName()).isEqualTo("Leading")
);
J.ParameterizedType returnType = (J.ParameterizedType) method.getReturnTypeExpression();
assertThat(returnType).isNotNull();
annotatedType = (J.AnnotatedType) returnType.getClazz();
assertThat(annotatedType.getAllAnnotations()).satisfiesExactly(
multi1 -> assertThat(multi1.getSimpleName()).isEqualTo("Multi1"),
multi2 -> assertThat(multi2.getSimpleName()).isEqualTo("Multi2")
);
})
)
);
}

}
21 changes: 15 additions & 6 deletions rewrite-java/src/main/java/org/openrewrite/java/tree/J.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,21 @@ public AnnotatedType withType(@Nullable JavaType type) {
return withTypeExpression(typeExpression.withType(type));
}

public List<Annotation> getAllAnnotations() {
List<J.Annotation> allAnnotations = annotations;
List<J.Annotation> moreAnnotations;
if (typeExpression instanceof FieldAccess &&
!(moreAnnotations = ((FieldAccess) typeExpression).getName().getAnnotations()).isEmpty()) {
if (allAnnotations.isEmpty()) {
allAnnotations = moreAnnotations;
} else {
allAnnotations = new ArrayList<>(annotations);
allAnnotations.addAll(moreAnnotations);
}
}
return allAnnotations;
}

@Override
public <P> J acceptJava(JavaVisitor<P> v, P p) {
return v.visitAnnotatedType(this, p);
Expand Down Expand Up @@ -3626,9 +3641,6 @@ public List<J.Annotation> getAllAnnotations() {
if (typeParameters != null) {
allAnnotations.addAll(typeParameters.getAnnotations());
}
if (returnTypeExpression instanceof AnnotatedType) {
allAnnotations.addAll(((AnnotatedType) returnTypeExpression).getAnnotations());
}
allAnnotations.addAll(name.getAnnotations());
return allAnnotations;
}
Expand Down Expand Up @@ -5661,9 +5673,6 @@ public List<J.Annotation> getAllAnnotations() {
for (J.Modifier modifier : modifiers) {
allAnnotations.addAll(modifier.getAnnotations());
}
if (typeExpression != null && typeExpression instanceof J.AnnotatedType) {
allAnnotations.addAll(((J.AnnotatedType) typeExpression).getAnnotations());
}
return allAnnotations;
}

Expand Down

0 comments on commit cba6919

Please sign in to comment.