/*
* Copyright 2013 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.errorprone.refaster;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableClassToInstanceMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.reflect.TypeToken;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.refaster.annotation.AlsoReverseTernary;
import com.google.errorprone.refaster.annotation.Matches;
import com.google.errorprone.refaster.annotation.NotMatches;
import com.google.errorprone.refaster.annotation.OfKind;
import com.google.errorprone.refaster.annotation.Repeated;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ArrayAccessTree;
import com.sun.source.tree.ArrayTypeTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.ConditionalExpressionTree;
import com.sun.source.tree.DoWhileLoopTree;
import com.sun.source.tree.EmptyStatementTree;
import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.ExpressionStatementTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.ForLoopTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.IfTree;
import com.sun.source.tree.InstanceOfTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.ParameterizedTypeTree;
import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.PrimitiveTypeTree;
import com.sun.source.tree.ReturnTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.SynchronizedTree;
import com.sun.source.tree.ThrowTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.tree.UnaryTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.tree.WhileLoopTree;
import com.sun.source.util.SimpleTreeVisitor;
import com.sun.tools.javac.code.Attribute.Compound;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.ArrayType;
import com.sun.tools.javac.code.Type.ClassType;
import com.sun.tools.javac.code.Type.ForAll;
import com.sun.tools.javac.code.Type.MethodType;
import com.sun.tools.javac.code.Type.TypeVar;
import com.sun.tools.javac.code.Type.WildcardType;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.model.AnnotationProxyMaker;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.util.Context;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.MirroredTypeException;
/**
* Converts a type-checked syntax tree to a portable {@code UTree} template.
*
* @author lowasser@google.com (Louis Wasserman)
*/
public class UTemplater extends SimpleTreeVisitor<UTree<?>, Void> {
/**
* Context key to indicate that templates should be treated as BlockTemplates, regardless
* of their structure.
*/
public static final Context.Key<Boolean> REQUIRE_BLOCK_KEY = new Context.Key<>();
/**
* Returns a template based on a method. One-line methods starting with a {@code return} statement
* are guessed to be expression templates, and all other methods are guessed to be block
* templates.
*/
public static Template<?> createTemplate(Context context, MethodTree decl) {
MethodSymbol declSym = ASTHelpers.getSymbol(decl);
ImmutableClassToInstanceMap<Annotation> annotations = UTemplater.annotationMap(declSym);
ImmutableMap<String, VarSymbol> freeExpressionVars = freeExpressionVariables(decl);
Context subContext = new SubContext(context);
if (annotations.containsKey(AlsoReverseTernary.class)) {
subContext.put(AlsoReverseTernary.class, annotations.getInstance(AlsoReverseTernary.class));
}
final UTemplater templater = new UTemplater(freeExpressionVars, subContext);
ImmutableMap<String, UType> expressionVarTypes = ImmutableMap.copyOf(
Maps.transformValues(freeExpressionVars, new Function<VarSymbol, UType>() {
@Override
public UType apply(VarSymbol sym) {
return templater.template(sym.type);
}
}));
UType genericType = templater.template(declSym.type);
List<UTypeVar> typeParameters;
UMethodType methodType;
if (genericType instanceof UForAll) {
UForAll forAllType = (UForAll) genericType;
typeParameters = forAllType.getTypeVars();
methodType = (UMethodType) forAllType.getQuantifiedType();
} else if (genericType instanceof UMethodType) {
typeParameters = ImmutableList.of();
methodType = (UMethodType) genericType;
} else {
throw new IllegalArgumentException(
"Expected genericType to be either a ForAll or a UMethodType, but was " + genericType);
}
List<? extends StatementTree> bodyStatements = decl.getBody().getStatements();
if (bodyStatements.size() == 1
&& Iterables.getOnlyElement(bodyStatements).getKind() == Kind.RETURN
&& context.get(REQUIRE_BLOCK_KEY) == null) {
ExpressionTree expression =
((ReturnTree) Iterables.getOnlyElement(bodyStatements)).getExpression();
return ExpressionTemplate.create(
annotations, typeParameters, expressionVarTypes,
templater.template(expression), methodType.getReturnType());
} else {
List<UStatement> templateStatements = new ArrayList<>();
for (StatementTree statement : bodyStatements) {
templateStatements.add(templater.template(statement));
}
return BlockTemplate.create(annotations,
typeParameters, expressionVarTypes, templateStatements);
}
}
public static ImmutableMap<String, VarSymbol> freeExpressionVariables(
MethodTree templateMethodDecl) {
ImmutableMap.Builder<String, VarSymbol> builder = ImmutableMap.builder();
for (VariableTree param : templateMethodDecl.getParameters()) {
builder.put(param.getName().toString(), ASTHelpers.getSymbol(param));
}
return builder.build();
}
private final ImmutableMap<String, VarSymbol> freeVariables;
private final Context context;
public UTemplater(Map<String, VarSymbol> freeVariables, Context context) {
this.freeVariables = ImmutableMap.copyOf(freeVariables);
this.context = context;
}
UTemplater(Context context) {
this(ImmutableMap.<String, VarSymbol>of(), context);
}
public UTree<?> template(Tree tree) {
return tree.accept(this, null);
}
private static <T> ImmutableList<T> cast(Iterable<?> elements, Class<T> clazz) {
ImmutableList.Builder<T> builder = ImmutableList.builder();
for (Object element : elements) {
builder.add(clazz.cast(element));
}
return builder.build();
}
@Override
public UMethodDecl visitMethod(MethodTree decl, Void v) {
return UMethodDecl.create(
visitModifiers(decl.getModifiers(), null),
decl.getName().toString(),
templateType(decl.getReturnType()),
cast(templateStatements(decl.getParameters()), UVariableDecl.class),
templateExpressions(decl.getThrows()),
(UBlock) template(decl.getBody()));
}
@Override
public UModifiers visitModifiers(ModifiersTree modifiers, Void v) {
return UModifiers.create(((JCModifiers) modifiers).flags,
cast(templateExpressions(modifiers.getAnnotations()), UAnnotation.class));
}
public UExpression template(ExpressionTree tree) {
return (UExpression) tree.accept(this, null);
}
@Nullable
private List<UExpression> templateExpressions(
@Nullable Iterable<? extends ExpressionTree> expressions) {
if (expressions == null) {
return null;
}
ImmutableList.Builder<UExpression> builder = ImmutableList.builder();
for (ExpressionTree expression : expressions) {
builder.add(template(expression));
}
return builder.build();
}
public UExpression templateType(Tree tree) {
checkArgument(tree instanceof ExpressionTree,
"Trees representing types are expected to implement ExpressionTree, but %s does not", tree);
return template((ExpressionTree) tree);
}
@Nullable
private List<UExpression> templateTypeExpressions(@Nullable Iterable<? extends Tree> types) {
if (types == null) {
return null;
}
ImmutableList.Builder<UExpression> builder = ImmutableList.builder();
for (Tree type : types) {
builder.add(templateType(type));
}
return builder.build();
}
@Override
public UInstanceOf visitInstanceOf(InstanceOfTree tree, Void v) {
return UInstanceOf.create(template(tree.getExpression()), template(tree.getType()));
}
@Override
public UPrimitiveTypeTree visitPrimitiveType(PrimitiveTypeTree tree, Void v) {
return UPrimitiveTypeTree.create(tree.getPrimitiveTypeKind());
}
@Override
public ULiteral visitLiteral(LiteralTree tree, Void v) {
return ULiteral.create(tree.getKind(), tree.getValue());
}
@Override
public UParens visitParenthesized(ParenthesizedTree tree, Void v) {
return UParens.create(template(tree.getExpression()));
}
@Override
public UAssign visitAssignment(AssignmentTree tree, Void v) {
return UAssign.create(template(tree.getVariable()), template(tree.getExpression()));
}
@Override
public UArrayAccess visitArrayAccess(ArrayAccessTree tree, Void v) {
return UArrayAccess.create(template(tree.getExpression()), template(tree.getIndex()));
}
@Override
public UAnnotation visitAnnotation(AnnotationTree tree, Void v) {
return UAnnotation.create(template(tree.getAnnotationType()),
templateExpressions(tree.getArguments()));
}
@Override
public UExpression visitMemberSelect(MemberSelectTree tree, Void v) {
Symbol sym = ASTHelpers.getSymbol(tree);
if (sym instanceof ClassSymbol) {
return UClassIdent.create((ClassSymbol) sym);
} else if (sym.isStatic()) {
ExpressionTree selected = tree.getExpression();
checkState(ASTHelpers.getSymbol(selected) instanceof ClassSymbol,
"Refaster cannot match static methods used on instances");
return staticMember(sym);
}
return UMemberSelect.create(template(tree.getExpression()),
tree.getIdentifier().toString(), template(sym.type));
}
private UStaticIdent staticMember(Symbol symbol) {
return UStaticIdent.create((ClassSymbol) symbol.getEnclosingElement(),
symbol.getSimpleName().toString(), template(symbol.asType()));
}
private static final UStaticIdent ANY_OF;
private static final UStaticIdent IS_INSTANCE;
private static final UStaticIdent CLAZZ;
private static final UStaticIdent NEW_ARRAY;
private static final UStaticIdent ENUM_VALUE_OF;
static {
UTypeVar tVar = UTypeVar.create("T");
ANY_OF = UStaticIdent.create(
Refaster.class.getCanonicalName(), "anyOf",
UForAll.create(ImmutableList.of(tVar), UMethodType.create(tVar, UArrayType.create(tVar))));
IS_INSTANCE = UStaticIdent.create(
Refaster.class.getCanonicalName(), "isInstance",
UForAll.create(ImmutableList.of(tVar),
UMethodType.create(UPrimitiveType.BOOLEAN,
UClassType.create(Object.class.getCanonicalName()))));
CLAZZ = UStaticIdent.create(
Refaster.class.getCanonicalName(), "clazz",
UForAll.create(ImmutableList.of(tVar),
UMethodType.create(UClassType.create(Class.class.getCanonicalName(), tVar))));
NEW_ARRAY = UStaticIdent.create(
Refaster.class.getCanonicalName(), "newArray",
UForAll.create(ImmutableList.of(tVar),
UMethodType.create(UArrayType.create(tVar), UPrimitiveType.INT)));
UTypeVar eVar = UTypeVar.create("E",
UClassType.create(Enum.class.getCanonicalName(), UTypeVar.create("E")));
ENUM_VALUE_OF = UStaticIdent.create(
Refaster.class.getCanonicalName(), "enumValueOf",
UForAll.create(ImmutableList.of(eVar),
UMethodType.create(eVar, UClassType.create(String.class.getCanonicalName()))));
}
private static Tree getSingleExplicitTypeArgument(MethodInvocationTree tree) {
if (tree.getTypeArguments().isEmpty()) {
throw new IllegalArgumentException("Methods in the Refaster class must be invoked with "
+ "an explicit type parameter; for example, 'Refaster.<T>isInstance(o)'.");
}
return Iterables.getOnlyElement(tree.getTypeArguments());
}
@Override
public UExpression visitMethodInvocation(MethodInvocationTree tree, Void v) {
if (ANY_OF.unify(tree.getMethodSelect(), new Unifier(context)) != null) {
return UAnyOf.create(templateExpressions(tree.getArguments()));
} else if (IS_INSTANCE.unify(tree.getMethodSelect(), new Unifier(context)) != null) {
return UInstanceOf.create(template(Iterables.getOnlyElement(tree.getArguments())),
template(getSingleExplicitTypeArgument(tree)));
} else if (CLAZZ.unify(tree.getMethodSelect(), new Unifier(context)) != null) {
Tree typeArg = getSingleExplicitTypeArgument(tree);
return UMemberSelect.create(templateType(typeArg), "class",
UClassType.create("java.lang.Class", template(((JCTree) typeArg).type)));
} else if (NEW_ARRAY.unify(tree.getMethodSelect(), new Unifier(context)) != null) {
Tree typeArg = getSingleExplicitTypeArgument(tree);
ExpressionTree lengthArg = Iterables.getOnlyElement(tree.getArguments());
return UNewArray.create(templateType(typeArg), ImmutableList.of(template(lengthArg)), null);
} else if (ENUM_VALUE_OF.unify(tree.getMethodSelect(), new Unifier(context)) != null) {
Tree typeArg = getSingleExplicitTypeArgument(tree);
ExpressionTree strArg = Iterables.getOnlyElement(tree.getArguments());
return UMethodInvocation.create(UMemberSelect.create(templateType(typeArg),
"valueOf", UMethodType.create(template(((JCTree) typeArg).type),
UClassType.create("java.lang.String"))), template(strArg));
} else {
return UMethodInvocation.create(template(tree.getMethodSelect()),
templateExpressions(tree.getArguments()));
}
}
@Override
public UBinary visitBinary(BinaryTree tree, Void v) {
return UBinary.create(tree.getKind(), template(tree.getLeftOperand()),
template(tree.getRightOperand()));
}
@Override
public UAssignOp visitCompoundAssignment(CompoundAssignmentTree tree, Void v) {
return UAssignOp.create(template(tree.getVariable()), tree.getKind(),
template(tree.getExpression()));
}
@Override
public UUnary visitUnary(UnaryTree tree, Void v) {
return UUnary.create(tree.getKind(), template(tree.getExpression()));
}
@Override
public UExpression visitConditionalExpression(ConditionalExpressionTree tree, Void v) {
UConditional result = UConditional.create(template(tree.getCondition()),
template(tree.getTrueExpression()), template(tree.getFalseExpression()));
if (context.get(AlsoReverseTernary.class) != null) {
return UAnyOf.create(result, result.reverse());
} else {
return result;
}
}
@Override
public UNewArray visitNewArray(NewArrayTree tree, Void v) {
return UNewArray.create((UExpression) template(tree.getType()),
templateExpressions(tree.getDimensions()),
templateExpressions(tree.getInitializers()));
}
@Override
public UNewClass visitNewClass(NewClassTree tree, Void v) {
return UNewClass.create(
tree.getEnclosingExpression() == null ? null : template(tree.getEnclosingExpression()),
templateTypeExpressions(tree.getTypeArguments()),
template(tree.getIdentifier()),
templateExpressions(tree.getArguments()),
(tree.getClassBody() == null) ? null : visitClass(tree.getClassBody(), null));
}
@Override
public UClassDecl visitClass(ClassTree tree, Void v) {
ImmutableList.Builder<UMethodDecl> decls = ImmutableList.builder();
for (MethodTree decl : Iterables.filter(tree.getMembers(), MethodTree.class)) {
if (decl.getReturnType() != null) {
decls.add(visitMethod(decl, null));
}
}
return UClassDecl.create(decls.build());
}
@Override
public UArrayTypeTree visitArrayType(ArrayTypeTree tree, Void v) {
return UArrayTypeTree.create(templateType(tree.getType()));
}
@Override
public UTypeApply visitParameterizedType(ParameterizedTypeTree tree, Void v) {
return UTypeApply.create(
templateType(tree.getType()),
templateTypeExpressions(tree.getTypeArguments()));
}
@Override
public UTypeCast visitTypeCast(TypeCastTree tree, Void v) {
return UTypeCast.create(template(tree.getType()), template(tree.getExpression()));
}
@Override
public UExpression visitIdentifier(IdentifierTree tree, Void v) {
Symbol sym = ASTHelpers.getSymbol(tree);
if (sym instanceof ClassSymbol) {
return UClassIdent.create((ClassSymbol) sym);
} else if (sym.isStatic()) {
return staticMember(sym);
} else if (freeVariables.containsKey(tree.getName().toString())) {
VarSymbol symbol = freeVariables.get(tree.getName().toString());
checkState(symbol == sym);
UExpression ident = UFreeIdent.create(tree.getName().toString());
Matches matches = ASTHelpers.getAnnotation(symbol, Matches.class);
if (matches != null) {
ident = UMatches.create(getValue(matches), true, ident);
}
NotMatches notMatches = ASTHelpers.getAnnotation(symbol, NotMatches.class);
if (notMatches != null) {
ident = UMatches.create(getValue(notMatches), false, ident);
}
OfKind hasKind = ASTHelpers.getAnnotation(symbol, OfKind.class);
if (hasKind != null) {
EnumSet<Kind> allowed = EnumSet.copyOf(Arrays.asList(hasKind.value()));
ident = UOfKind.create(ident, ImmutableSet.copyOf(allowed));
}
// @Repeated annotations need to be checked last.
Repeated repeated = ASTHelpers.getAnnotation(symbol, Repeated.class);
if (repeated != null) {
ident = URepeated.create(tree.getName().toString(), ident);
}
return ident;
}
switch (sym.getKind()) {
case TYPE_PARAMETER:
return UTypeVarIdent.create(tree.getName().toString());
default:
return ULocalVarIdent.create(tree.getName().toString());
}
}
/**
* Returns the {@link Class} instance for the {@link Matcher} associated with the provided
* {@link Matches} annotation. This roundabout solution is recommended and explained by
* {@link Element#getAnnotation(Class)}.
*/
private Class<? extends Matcher<? super ExpressionTree>> getValue(Matches matches) {
String name;
try {
matches.value();
throw new RuntimeException("unreachable");
} catch (MirroredTypeException e) {
DeclaredType type = (DeclaredType) e.getTypeMirror();
name = ((TypeElement) type.asElement()).getQualifiedName().toString();
}
try {
return asSubclass(Class.forName(name), new TypeToken<Matcher<? super ExpressionTree>>() {});
} catch (ClassNotFoundException|ClassCastException e) {
throw new RuntimeException(e);
}
}
/**
* Returns the {@link Class} instance for the {@link Matcher} associated with the provided
* {@link NotMatches} annotation. This roundabout solution is recommended and explained by
* {@link Element#getAnnotation(Class)}.
*/
private Class<? extends Matcher<? super ExpressionTree>> getValue(NotMatches matches) {
String name;
try {
matches.value();
throw new RuntimeException("unreachable");
} catch (MirroredTypeException e) {
DeclaredType type = (DeclaredType) e.getTypeMirror();
name = ((TypeElement) type.asElement()).getQualifiedName().toString();
}
try {
return asSubclass(Class.forName(name), new TypeToken<Matcher<? super ExpressionTree>>() {});
} catch (ClassNotFoundException|ClassCastException e) {
throw new RuntimeException(e);
}
}
/**
* Similar to {@link Class#asSubclass(Class)}, but it accepts a {@link TypeToken} so it handles
* generics better.
*/
@SuppressWarnings("unchecked")
private <T> Class<? extends T> asSubclass(Class<?> klass, TypeToken<T> token)
throws ClassCastException{
if (!token.isAssignableFrom(klass)) {
throw new ClassCastException(klass + " is not assignable to " + token);
}
return (Class<? extends T>) klass;
}
public UStatement template(StatementTree tree) {
return (UStatement) tree.accept(this, null);
}
@Nullable
private List<UStatement> templateStatements(@Nullable List<? extends StatementTree> statements) {
if (statements == null) {
return null;
}
ImmutableList.Builder<UStatement> builder = ImmutableList.builder();
for (StatementTree statement : statements) {
builder.add(template(statement));
}
return builder.build();
}
@Override
public UExpressionStatement visitExpressionStatement(ExpressionStatementTree tree, Void v) {
return UExpressionStatement.create(template(tree.getExpression()));
}
@Override
public UReturn visitReturn(ReturnTree tree, Void v) {
return UReturn.create(
(tree.getExpression() == null) ? null : template(tree.getExpression()));
}
@Override
public UWhileLoop visitWhileLoop(WhileLoopTree tree, Void v) {
return UWhileLoop.create(template(tree.getCondition()), template(tree.getStatement()));
}
@Override
public UVariableDecl visitVariable(VariableTree tree, Void v) {
return UVariableDecl.create(
tree.getName().toString(),
templateType(tree.getType()),
(tree.getInitializer() == null) ? null : template(tree.getInitializer()));
}
@Override
public USkip visitEmptyStatement(EmptyStatementTree tree, Void v) {
return USkip.INSTANCE;
}
@Override
public UForLoop visitForLoop(ForLoopTree tree, Void v) {
return UForLoop.create(
templateStatements(tree.getInitializer()),
(tree.getCondition() == null) ? null : template(tree.getCondition()),
cast(templateStatements(tree.getUpdate()), UExpressionStatement.class),
template(tree.getStatement()));
}
@Override
public UBlock visitBlock(BlockTree tree, Void v) {
return UBlock.create(templateStatements(tree.getStatements()));
}
@Override
public UThrow visitThrow(ThrowTree tree, Void v) {
return UThrow.create(template(tree.getExpression()));
}
@Override
public UDoWhileLoop visitDoWhileLoop(DoWhileLoopTree tree, Void v) {
return UDoWhileLoop.create(template(tree.getStatement()), template(tree.getCondition()));
}
@Override
public UEnhancedForLoop visitEnhancedForLoop(EnhancedForLoopTree tree, Void v) {
return UEnhancedForLoop.create(
visitVariable(tree.getVariable(), null),
template(tree.getExpression()),
template(tree.getStatement()));
}
@Override
public USynchronized visitSynchronized(SynchronizedTree tree, Void v) {
return USynchronized.create(
template(tree.getExpression()),
visitBlock(tree.getBlock(), null));
}
@Override
public UIf visitIf(IfTree tree, Void v) {
return UIf.create(
template(tree.getCondition()),
template(tree.getThenStatement()),
(tree.getElseStatement() == null) ? null : template(tree.getElseStatement()));
}
@Override
protected UTree<?> defaultAction(Tree tree, Void v) {
throw new IllegalArgumentException(
"Refaster does not currently support syntax " + tree.getClass());
}
public UType template(Type type) {
return type.accept(typeTemplater, null);
}
private List<UType> templateTypes(Iterable<? extends Type> types) {
ImmutableList.Builder<UType> builder = ImmutableList.builder();
for (Type ty : types) {
builder.add(template(ty));
}
return builder.build();
}
private final Type.Visitor<UType, Void> typeTemplater = new Types.SimpleVisitor<UType, Void>() {
private final Map<TypeSymbol, UTypeVar> typeVariables = new HashMap<>();
@Override
public UType visitType(Type type, Void v) {
if (UPrimitiveType.isDeFactoPrimitive(type.getKind())) {
return UPrimitiveType.create(type.getKind());
} else {
throw new IllegalArgumentException(
"Refaster does not currently support syntax " + type.getKind());
}
}
@Override
public UArrayType visitArrayType(ArrayType type, Void v) {
return UArrayType.create(type.getComponentType().accept(this, null));
}
@Override
public UMethodType visitMethodType(MethodType type, Void v) {
return UMethodType.create(
type.getReturnType().accept(this, null), templateTypes(type.getParameterTypes()));
}
@Override
public UClassType visitClassType(ClassType type, Void v) {
return UClassType.create(
type.tsym.getQualifiedName().toString(), templateTypes(type.getTypeArguments()));
}
@Override
public UWildcardType visitWildcardType(WildcardType type, Void v) {
return UWildcardType.create(type.kind, type.type.accept(this, null));
}
@Override
public UTypeVar visitTypeVar(TypeVar type, Void v) {
/*
* In order to handle recursively bounded type variables without a stack overflow, we first
* cache a type var with no bounds, then we template the bounds.
*/
TypeSymbol tsym = type.asElement();
if (typeVariables.containsKey(tsym)) {
return typeVariables.get(tsym);
}
UTypeVar var = UTypeVar.create(tsym.getSimpleName().toString());
typeVariables.put(tsym, var); // so the type variable can be used recursively in the bounds
var.setLowerBound(type.getLowerBound().accept(this, null));
var.setUpperBound(type.getUpperBound().accept(this, null));
return var;
}
@Override
public UForAll visitForAll(ForAll type, Void v) {
List<UTypeVar> vars = cast(templateTypes(type.getTypeVariables()), UTypeVar.class);
return UForAll.create(vars, type.qtype.accept(this, null));
}
};
@SuppressWarnings("unchecked")
public static ImmutableClassToInstanceMap<Annotation> annotationMap(Symbol symbol) {
ImmutableClassToInstanceMap.Builder<Annotation> builder = ImmutableClassToInstanceMap.builder();
for (Compound compound : symbol.getAnnotationMirrors()) {
Name qualifiedAnnotationType =
((TypeElement) compound.getAnnotationType().asElement()).getQualifiedName();
try {
Class<? extends Annotation> annotationClazz =
Class.forName(qualifiedAnnotationType.toString()).asSubclass(Annotation.class);
builder.put((Class) annotationClazz,
AnnotationProxyMaker.generateAnnotation(compound, annotationClazz));
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Unrecognized annotation type", e);
}
}
return builder.build();
}
}