Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add annotations to JavaType$Array #3860

Merged
merged 3 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2126,15 +2126,11 @@ private String skip(@Nullable String token) {
}

private <T extends TypeTree & Expression> T typeTree(@Nullable ClassNode classNode) {
Space prefix = whitespace();
if (classNode != null && classNode.isArray()) {
//noinspection unchecked
return (T) new J.ArrayType(randomId(), prefix, Markers.EMPTY,
typeTree(classNode.getComponentType()),
null,
padLeft(sourceBefore("["), sourceBefore("]")),
typeMapping.type(classNode));
return (T) arrayType(classNode);
}
Space prefix = whitespace();
String maybeFullyQualified = name();
String[] parts = maybeFullyQualified.split("\\.");

Expand Down Expand Up @@ -2178,6 +2174,40 @@ private <T extends TypeTree & Expression> T typeTree(@Nullable ClassNode classNo
return expr.withPrefix(prefix);
}

private TypeTree arrayType(ClassNode classNode) {
ClassNode typeTree = classNode.getComponentType();
int count = 1;
while (typeTree.isArray()) {
count++;
typeTree = typeTree.getComponentType();
}
Space prefix = whitespace();
TypeTree elemType = typeTree(typeTree);
JLeftPadded<Space> dimension = padLeft(sourceBefore("["), sourceBefore("]"));
return new J.ArrayType(randomId(), prefix, Markers.EMPTY,
count == 1 ? elemType : mapDimensions(elemType, classNode.getComponentType()),
null,
dimension,
typeMapping.type(classNode));
}

private TypeTree mapDimensions(TypeTree baseType, ClassNode classNode) {
if (classNode.isArray()) {
Space prefix = whitespace();
JLeftPadded<Space> dimension = padLeft(sourceBefore("["), sourceBefore("]"));
return new J.ArrayType(
randomId(),
prefix,
Markers.EMPTY,
mapDimensions(baseType, classNode.getComponentType()),
null,
dimension,
typeMapping.type(classNode)
);
}
return baseType;
}

private Space sourceBefore(String untilDelim) {
int delimIndex = positionOfNext(untilDelim);
if (delimIndex < 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,13 @@ private JavaType parameterizedType(ClassNode type, String signature) {
}

private JavaType.Array arrayType(ClassNode array, String signature) {
JavaType.Array arr = new JavaType.Array(null, null);
JavaType.Array arr = new JavaType.Array(null, null, null);
typeCache.put(signature, arr);

if (array.getComponentType().isUsingGenerics()) {
arr.unsafeSet(type(array.getComponentType().getGenericsTypes()[0]));
arr.unsafeSet(type(array.getComponentType().getGenericsTypes()[0]), null);
} else {
arr.unsafeSet(type(array.getComponentType()));
arr.unsafeSet(type(array.getComponentType()), null);
}

return arr;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public J visitArrayAccess(ArrayAccessTree node, Space fmt) {

@Override
public J visitArrayType(ArrayTypeTree node, Space fmt) {
return arrayTypeTree((JCArrayTypeTree) node, new HashMap<>()).withPrefix(fmt);
return arrayTypeTree(node, new HashMap<>()).withPrefix(fmt);
}

@Override
Expand Down Expand Up @@ -1203,6 +1203,7 @@ public J visitTypeCast(TypeCastTree node, Space fmt) {
convert(node.getType(), t -> sourceBefore(")"))),
convert(node.getExpression()));
}

@Override
public J visitAnnotatedType(AnnotatedTypeTree node, Space fmt) {
Map<Integer, JCAnnotation> annotationPosTable = mapAnnotations(node.getAnnotations(),
Expand All @@ -1212,7 +1213,7 @@ public J visitAnnotatedType(AnnotatedTypeTree node, Space fmt) {
if (node.getUnderlyingType() instanceof JCFieldAccess) {
return new J.AnnotatedType(randomId(), fmt, Markers.EMPTY, leadingAnnotations, annotatedTypeTree(node.getUnderlyingType(), annotationPosTable));
} else if (node.getUnderlyingType() instanceof JCArrayTypeTree) {
return new J.AnnotatedType(randomId(), fmt, Markers.EMPTY, leadingAnnotations, arrayTypeTree((JCArrayTypeTree) node.getUnderlyingType(), annotationPosTable));
return new J.AnnotatedType(randomId(), fmt, Markers.EMPTY, leadingAnnotations, arrayTypeTree(node, annotationPosTable));
}
}
return new J.AnnotatedType(randomId(), fmt, Markers.EMPTY, leadingAnnotations, convert(node.getUnderlyingType()));
Expand Down Expand Up @@ -1258,45 +1259,66 @@ private TypeTree annotatedTypeTree(Tree node, Map<Integer, JCAnnotation> annotat
annotations, fieldAccess.name.toString(), type, typeMapping.variableType(fieldAccess.sym))),
type
);
} else if (node instanceof JCAnnotatedType) {
JCAnnotatedType annotatedType = (JCAnnotatedType) node;
if (annotatedType.getUnderlyingType() instanceof JCArrayTypeTree) {
return arrayTypeTree((JCArrayTypeTree) annotatedType.getUnderlyingType(),
mapAnnotations(annotatedType.getAnnotations(), annotationPosTable));
}
}
return convert(node);
}

private TypeTree arrayTypeTree(JCArrayTypeTree arrayTypeTree, Map<Integer, JCAnnotation> annotationPosTable) {
Space prefix = whitespace();
Tree typeIdent = arrayTypeTree.getType();
while (!annotationPosTable.isEmpty() && typeIdent instanceof ArrayTypeTree) {
typeIdent = ((ArrayTypeTree) typeIdent).getType();
private TypeTree arrayTypeTree(Tree tree, Map<Integer, JCAnnotation> annotationPosTable) {
Tree typeIdent = tree;
int count = 0;
JCArrayTypeTree arrayTypeTree = null;
while (typeIdent instanceof JCAnnotatedType || typeIdent instanceof JCArrayTypeTree) {
if (typeIdent instanceof JCAnnotatedType) {
typeIdent = ((JCAnnotatedType) typeIdent).getUnderlyingType();
}
if (typeIdent instanceof JCArrayTypeTree) {
if (count == 0) {
arrayTypeTree = (JCArrayTypeTree) typeIdent;
}
count++;
typeIdent = ((JCArrayTypeTree) typeIdent).getType();
}
}

TypeTree elemType = annotatedTypeTree(typeIdent, annotationPosTable);
Space prefix = whitespace();
TypeTree elemType = convert(typeIdent);
List<J.Annotation> annotations = leadingAnnotations(annotationPosTable);
int saveCursor = cursor;
Space before = whitespace();
JLeftPadded<Space> dimension;
if (source.startsWith("[", cursor)) {
skip("[");
dimension = padLeft(before, sourceBefore("]"));
} else {
cursor = saveCursor;
return elemType;
JLeftPadded<Space> dimension = padLeft(sourceBefore("["), sourceBefore("]"));
assert arrayTypeTree != null;
return new J.ArrayType(randomId(), prefix, Markers.EMPTY,
count == 1 ? elemType : mapDimensions(elemType, arrayTypeTree.getType(), annotationPosTable),
annotations,
dimension,
typeMapping.type(tree));
}

private TypeTree mapDimensions(TypeTree baseType, Tree tree, Map<Integer, JCAnnotation> annotationPosTable) {
Tree typeIdent = tree;
if (typeIdent instanceof JCAnnotatedType) {
mapAnnotations(((JCAnnotatedType) typeIdent).getAnnotations(), annotationPosTable);
typeIdent = ((JCAnnotatedType) typeIdent).getUnderlyingType();
}

return new J.ArrayType(
randomId(),
prefix,
Markers.EMPTY,
elemType,
annotations.isEmpty() ? null : annotations,
dimension,
typeMapping.type(arrayTypeTree)
);
if (typeIdent instanceof JCArrayTypeTree) {
List<J.Annotation> annotations = leadingAnnotations(annotationPosTable);
int saveCursor = cursor;
whitespace();
if (source.startsWith("[", cursor)) {
cursor = saveCursor;
JLeftPadded<Space> dimension = padLeft(sourceBefore("["), sourceBefore("]"));
return new J.ArrayType(
randomId(),
EMPTY,
Markers.EMPTY,
mapDimensions(baseType, ((JCArrayTypeTree) typeIdent).elemtype, annotationPosTable),
annotations,
dimension,
typeMapping.type(tree)
);
}
cursor = saveCursor;
}
return baseType;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,74 @@ private JavaType intersectionType(Type.IntersectionClassType type, String signat
}

private JavaType array(Type type, String signature) {
JavaType.Array arr = new JavaType.Array(null, null);
JavaType.Array arr = new JavaType.Array(null, null, null);
typeCache.put(signature, arr);
arr.unsafeSet(type(((Type.ArrayType) type).elemtype));
arr.unsafeSet(type(((Type.ArrayType) type).elemtype), null);
return arr;
}

/**
* Maps annotated array types to a JavaType through the JCTree instead of the Type tree.
* <p>
* The JCTree is necessary to preserve annotations on multidimensional arrays in Java 8.
* In Java 11+, annotations are available directly on the `Type` tree through TypeMetadata, but
* annotations are accessed differently in Java 11 and 17 compared to Java 21.
* Annotated array types are mapped through the JCTree so that the mapping is consistent for each version of the
* java compiler.
*/
private JavaType annotatedArray(JCTree.JCAnnotatedType annotatedType) {
String signature = signatureBuilder.annotatedArraySignature(annotatedType);
JavaType.Array existing = typeCache.get(signature);
if (existing != null) {
return existing;
}
JavaType.Array arr = new JavaType.Array(null, null, null);
typeCache.put(signature, arr);

Tree tree = annotatedType;
List<Tree> trees = new ArrayList<>();
while (tree instanceof JCTree.JCAnnotatedType || tree instanceof JCTree.JCArrayTypeTree) {
if (tree instanceof JCTree.JCAnnotatedType) {
if (((JCTree.JCAnnotatedType) tree).getUnderlyingType() instanceof JCTree.JCArrayTypeTree) {
trees.add(0, tree);
tree = ((JCTree.JCArrayTypeTree) ((JCTree.JCAnnotatedType) tree).getUnderlyingType()).getType();
} else {
tree = ((JCTree.JCAnnotatedType) tree).getUnderlyingType();
}
} else {
trees.add(0, tree);
tree = ((JCTree.JCArrayTypeTree) tree).getType();
}
}
return mapElements(type(tree), trees);
}

private JavaType mapElements(JavaType elementType, List<Tree> trees) {
int count = trees.size();
if (count == 0) {
return elementType;
}
return mapElements(
new JavaType.Array(
null,
elementType,
trees.get(0) instanceof JCTree.JCAnnotatedType ? mapAnnotations(((JCTree.JCAnnotatedType) trees.get(0)).annotations) : null
),
trees.subList(1, count)
);
}

private @Nullable JavaType.FullyQualified[] mapAnnotations(List<JCTree.JCAnnotation> annotations) {
List<JavaType.FullyQualified> types = new ArrayList<>(annotations.size());
for (JCTree.JCAnnotation annotation : annotations) {
JavaType.FullyQualified fq = TypeUtils.asFullyQualified(type(annotation));
if (fq != null) {
types.add(fq);
}
}
return types.toArray(new JavaType.FullyQualified[0]);
}

private JavaType.GenericTypeVariable generic(Type.WildcardType wildcard, String signature) {
JavaType.GenericTypeVariable gtv = new JavaType.GenericTypeVariable(null, "?", INVARIANT, null);
typeCache.put(signature, gtv);
Expand Down Expand Up @@ -288,6 +350,8 @@ public JavaType type(@Nullable Tree tree) {
symbol = ((JCTree.JCMethodDecl) tree).sym;
} else if (tree instanceof JCTree.JCVariableDecl) {
return variableType(((JCTree.JCVariableDecl) tree).sym);
} else if (tree instanceof JCTree.JCAnnotatedType && ((JCTree.JCAnnotatedType) tree).getUnderlyingType() instanceof JCTree.JCArrayTypeTree) {
return annotatedArray((JCTree.JCAnnotatedType) tree);
}

return type(((JCTree) tree).type, symbol);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,15 @@
*/
package org.openrewrite.java.isolated;

import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.tree.JCTree;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.JavaTypeSignatureBuilder;
import org.openrewrite.java.tree.JavaType;

import javax.lang.model.type.NullType;
import javax.lang.model.type.TypeMirror;
import java.util.HashSet;
import java.util.Set;
import java.util.StringJoiner;
import java.util.*;

class ReloadableJava11TypeSignatureBuilder implements JavaTypeSignatureBuilder {
@Nullable
Expand Down Expand Up @@ -81,7 +78,41 @@ private void completeClassSymbol(Symbol.ClassSymbol classSymbol) {

@Override
public String arraySignature(Object type) {
return signature(((Type.ArrayType) type).elemtype) + "[]";
Object elemType = type;
StringBuilder dimensions = new StringBuilder();
while (elemType instanceof Type.ArrayType) {
dimensions.append("[]");
elemType = ((Type.ArrayType) elemType).elemtype;
}
return signature(elemType) + dimensions;
}

public String annotatedArraySignature(JCTree.JCAnnotatedType annotatedType) {
JCTree tree = annotatedType;
StringBuilder dimensions = new StringBuilder();
while (tree instanceof JCTree.JCAnnotatedType || tree instanceof JCTree.JCArrayTypeTree) {
if (tree instanceof JCTree.JCAnnotatedType) {
dimensions.append(mapAnnotations(((JCTree.JCAnnotatedType) tree).annotations));
tree = ((JCTree.JCAnnotatedType) tree).getUnderlyingType();
}
if (tree instanceof JCTree.JCArrayTypeTree) {
dimensions.append("[]");
tree = ((JCTree.JCArrayTypeTree) tree).getType();
}
}
return signature(tree.type) + dimensions;
}

private String mapAnnotations(List<JCTree.JCAnnotation> annotations) {
if (annotations.isEmpty()) {
return "";
}

StringJoiner joiner = new StringJoiner(",", "[", "]");
for (JCTree.JCAnnotation annotation : annotations) {
joiner.add(signature(annotation.type));
}
return joiner.toString();
}

@Override
Expand Down
Loading