package checkers.types; import java.util.Collections; import java.util.LinkedList; import java.util.List; import javax.lang.model.element.*; import javax.lang.model.type.TypeKind; import checkers.types.AnnotatedTypeMirror.*; import checkers.util.TypesUtils; import com.sun.tools.javac.code.Attribute; import com.sun.tools.javac.code.TargetType; import com.sun.tools.javac.code.TypeAnnotationPosition; import com.sun.tools.javac.code.Symbol.*; /** * A utility class used to extract the annotations from an element and inserts * them into the elements type. */ // // The method of this class fails silently when a bound or an index is out of // bound. This is done defensively against the compiler bugs. JSR 308 // compilers prior to 0.7.3 didn't properly fill TypeAnnotations fields. // After some weeks/months, these checks should be removed once we ensure // that the compiler is not buggy // // Note written on 07/07/2008 by Mahmood public class TypeFromElement { /** * Extracts type annotations from the element and inserts them into the * type of the element. * * The element could be one of: * 1. {@link TypeElement} of a class or an interface * 2. {@link ExecutableElement} of a method or a constructor * 3. {@link VariableElement} of a field or a method parameter * 4. {@link TypeParameterElement} of a method or a class type parameter * */ @SuppressWarnings("deprecation") public static void annotate(AnnotatedTypeMirror type, Element element) { if (element == null) throw new IllegalArgumentException("element cannot be null"); else if (element.getKind().isField()) annotate(type, (VariableElement) element); else if (element instanceof TypeElement) annotate((AnnotatedDeclaredType)type, (TypeElement) element); else if (element instanceof ExecutableElement) annotate((AnnotatedExecutableType)type, (ExecutableElement)element); else if (element.getKind() == ElementKind.PARAMETER) { Element enclosing = element.getEnclosingElement(); if (enclosing instanceof ExecutableElement) { ExecutableElement execElt = (ExecutableElement)enclosing; if (execElt.getParameters().contains(element)) { int param_index = execElt.getParameters().indexOf(element); for (Attribute.TypeCompound typeAnno : ((MethodSymbol)execElt).typeAnnotations) if ((typeAnno.position.type == TargetType.METHOD_PARAMETER_GENERIC_OR_ARRAY) && typeAnno.position.parameter_index == param_index) annotate(type, typeAnno); } } } else if (element.getKind() == ElementKind.TYPE_PARAMETER) { Element enclosing = element.getEnclosingElement(); if (enclosing instanceof TypeElement) { TypeElement clsElt = (TypeElement)enclosing; if (clsElt.getTypeParameters().contains(element)) { int param_index = clsElt.getTypeParameters().indexOf(element); for (Attribute.TypeCompound typeAnno : ((ClassSymbol)clsElt).typeAnnotations) { switch (typeAnno.position.type) { case CLASS_TYPE_PARAMETER_BOUND: case CLASS_TYPE_PARAMETER_BOUND_GENERIC_OR_ARRAY: if (typeAnno.position.parameter_index == param_index) annotatePossibleBound(type, typeAnno); } } } } else if (enclosing instanceof ExecutableElement) { ExecutableElement execElt = (ExecutableElement)enclosing; if (execElt.getTypeParameters().contains(element)) { int param_index = execElt.getTypeParameters().indexOf(element); for (Attribute.TypeCompound typeAnno : ((MethodSymbol)execElt).typeAnnotations) { switch (typeAnno.position.type) { case METHOD_TYPE_PARAMETER: //case METHOD_TYPE_PARAMETER_GENERIC_OR_ARRAY: case METHOD_TYPE_PARAMETER_BOUND: case METHOD_TYPE_PARAMETER_BOUND_GENERIC_OR_ARRAY: if (typeAnno.position.parameter_index == param_index) annotatePossibleBound(type, typeAnno); } } } } } else throw new IllegalArgumentException("illegal argument: " + element.getKind()); } /** * Extracts type annotations from the element and inserts them into the * type of the element. * * the element needs to be that of a field * @param type the type of the field * @param element the element of a field */ @SuppressWarnings("deprecation") public static void annotate(AnnotatedTypeMirror type, VariableElement element) { if (!element.getKind().isField()) throw new IllegalArgumentException("element is not a field: " + element.getKind()); VarSymbol symbol = (VarSymbol)element; addAnnotationsToElt(type, symbol.getAnnotationMirrors()); for (Attribute.TypeCompound anno : symbol.typeAnnotations) { TypeAnnotationPosition pos = anno.position; switch (pos.type) { //case FIELD: case FIELD_GENERIC_OR_ARRAY: annotate(type, anno); break; } } } /** * Extracts type annotations from the element and inserts them into the * type of the element. * * the element needs to be that of a class or an interface * * @param type the type of the class/interface * @param element the element of a class/interface */ public static void annotate(AnnotatedDeclaredType type, TypeElement element) { ClassSymbol symbol = (ClassSymbol) element; // Annotate raw types type.addAnnotations(symbol.getAnnotationMirrors()); List<AnnotatedTypeMirror> typeParameters = type.getTypeArguments(); for (Attribute.TypeCompound anno : symbol.typeAnnotations) { TypeAnnotationPosition pos = anno.position; switch(pos.type) { case CLASS_TYPE_PARAMETER: if (pos.parameter_index >= 0 && pos.parameter_index < typeParameters.size()) { AnnotatedTypeMirror typeParam = typeParameters.get(pos.parameter_index); typeParam.addAnnotation(anno); if (typeParam.getKind() == TypeKind.TYPEVAR) ((AnnotatedTypeVariable)typeParam).getUpperBound().addAnnotation(anno); } break; case CLASS_TYPE_PARAMETER_BOUND: case CLASS_TYPE_PARAMETER_BOUND_GENERIC_OR_ARRAY: if (pos.parameter_index >= 0 && pos.parameter_index < typeParameters.size()) { List<AnnotatedTypeMirror> bounds = getBounds(typeParameters.get(pos.parameter_index)); if (pos.bound_index >= 0 && pos.bound_index < bounds.size()) annotate(bounds.get(pos.bound_index), anno); } break; } } } public static void annotateSupers(List<AnnotatedDeclaredType> supertypes, TypeElement element) { final ClassSymbol symbol = (ClassSymbol)element; // Add the ones in the extends clauses final boolean hasSuperClass = element.getSuperclass().getKind() != TypeKind.NONE; AnnotatedDeclaredType superClassType = hasSuperClass ? supertypes.get(0) : null; List<AnnotatedDeclaredType> superInterfaces = hasSuperClass ? tail(supertypes) : supertypes; for (Attribute.TypeCompound anno : symbol.typeAnnotations) { TypeAnnotationPosition pos = anno.position; switch(pos.type) { case CLASS_EXTENDS: case CLASS_EXTENDS_GENERIC_OR_ARRAY: if (pos.type_index == -1 && superClassType != null) annotate(superClassType, anno); else if (pos.type_index >= 0 && pos.type_index < superInterfaces.size()) annotate(superInterfaces.get(pos.type_index), anno); break; } } } /** * Extracts type annotations from the element and inserts them into the * type of the element. * * the element needs to be that of a method or a constructor. * * @param type the type of the method * @param element the element of a method */ public static void annotate(AnnotatedExecutableType type, ExecutableElement element) { MethodSymbol symbol = (MethodSymbol) element; // Add annotations on the return type addAnnotationsToElt(type.getReturnType(), symbol.getAnnotationMirrors()); // Add annotations on the param raws final List<AnnotatedTypeMirror> thrown = type.getThrownTypes(); final List<AnnotatedTypeMirror> params = type.getParameterTypes(); final List<AnnotatedTypeVariable> typeParams = type.getTypeVariables(); for (int i = 0; i < params.size(); ++i) addAnnotationsToElt(params.get(i), element.getParameters().get(i).getAnnotationMirrors()); for (Attribute.TypeCompound typeAnno : symbol.typeAnnotations) { final TypeAnnotationPosition pos = typeAnno.position; switch (typeAnno.position.type) { case METHOD_RECEIVER: //case METHOD_RECEIVER_GENERIC_OR_ARRAY: annotate(type.getReceiverType(), typeAnno); break; case METHOD_RETURN: case METHOD_RETURN_GENERIC_OR_ARRAY: annotate(type.getReturnType(), typeAnno); break; case METHOD_PARAMETER: case METHOD_PARAMETER_GENERIC_OR_ARRAY: if (pos.parameter_index >= 0 && pos.parameter_index < params.size()) annotate(params.get(pos.parameter_index), typeAnno); break; case THROWS: //case THROWS_GENERIC_OR_ARRAY: if (pos.type_index >= 0 && pos.type_index < thrown.size()) annotate(thrown.get(pos.type_index), typeAnno); break; case METHOD_TYPE_PARAMETER: //case METHOD_TYPE_PARAMETER_GENERIC_OR_ARRAY: if (pos.parameter_index >= 0 && pos.parameter_index < typeParams.size()) annotate(typeParams.get(pos.parameter_index), typeAnno); break; case METHOD_TYPE_PARAMETER_BOUND: case METHOD_TYPE_PARAMETER_BOUND_GENERIC_OR_ARRAY: if (pos.parameter_index >= 0 && pos.parameter_index < typeParams.size()) { List<AnnotatedTypeMirror> bounds = getBounds(typeParams.get(pos.parameter_index)); if (pos.bound_index >= 0 && pos.bound_index < bounds.size()) annotate(bounds.get(pos.bound_index), typeAnno); } break; case WILDCARD_BOUND: case WILDCARD_BOUND_GENERIC_OR_ARRAY: // TODO: Handle these cases break; } } } static void addAnnotationsToElt(AnnotatedTypeMirror type, List<? extends AnnotationMirror> annotations) { // Annotate the inner most array AnnotatedTypeMirror innerType = type; while (innerType.getKind() == TypeKind.ARRAY) innerType = ((AnnotatedArrayType)innerType).getComponentType(); innerType.addAnnotations(annotations); } /** * Extracts type annotations from the element and inserts them into the * type of the element. * * the element needs to be that of a field * @param type the type of the field * @param element the element of a field */ private static void annotate(AnnotatedTypeMirror type, Attribute.TypeCompound anno) { TypeAnnotationPosition pos = anno.position; if (!pos.type.hasLocation()) { type.addAnnotation(anno); } else annotate(type, pos.location, Collections.singletonList(anno)); } private static void annotatePossibleBound(AnnotatedTypeMirror type, Attribute.TypeCompound anno) { if (!anno.position.type.hasBound()) annotate(type, anno); else { if (type.getKind() != TypeKind.TYPEVAR && type.getKind() != TypeKind.WILDCARD) return; List<AnnotatedTypeMirror> bounds = getBounds(type); int boundIndex = anno.position.bound_index; if (boundIndex >= 0 && boundIndex < bounds.size()) annotate(bounds.get(boundIndex), anno); } } private static void annotate(AnnotatedTypeMirror type, List<Integer> location, List<? extends AnnotationMirror> annotations) { if (location.isEmpty()) type.addAnnotations(annotations); else if (type.getKind() == TypeKind.DECLARED) annotate((AnnotatedDeclaredType)type, location, annotations); else if (type.getKind() == TypeKind.ARRAY) annotate((AnnotatedArrayType)type, location, annotations); else throw new AssertionError("only declared and arrays can have assertions"); } private static void annotate(AnnotatedDeclaredType type, List<Integer> location, List<? extends AnnotationMirror> annotations) { if (location.isEmpty()) type.addAnnotations(annotations); else if (location.get(0) < type.getTypeArguments().size()) annotate(type.getTypeArguments().get(location.get(0)), tail(location), annotations); } // Dealing with arrays requires much testing private static void annotate(AnnotatedArrayType type, List<Integer> location, List<? extends AnnotationMirror> annotations) { if (location.isEmpty()) type.addAnnotations(annotations); else if (location.size() == 1) { int arrayIndex = location.get(0); List<AnnotatedTypeMirror> arrays = createArraysList(type); if (arrayIndex < arrays.size()) arrays.get(arrayIndex).addAnnotations(annotations); } else throw new AssertionError("location too large: " + location); } private static List<AnnotatedTypeMirror> createArraysList(AnnotatedArrayType array) { LinkedList<AnnotatedTypeMirror> arrays = new LinkedList<AnnotatedTypeMirror>(); // I think that type can never be null AnnotatedTypeMirror type = array; while (type != null && type.getKind() == TypeKind.ARRAY) { arrays.addFirst(type); type = ((AnnotatedArrayType)type).getComponentType(); } // adding the component type if (type != null) arrays.addFirst(type); return arrays; } private static <T> List<T> tail(List<T> list) { return list.subList(1, list.size()); } /** * Return the bounds for the type and needs to be either * {@link AnnotatedTypeVariable} or * {@link AnnotatedWildcard}. If the type has no bounds, it returns an * empty list. * * @param type a wildcard or type variable * @return bounds of a type variable */ private static List<AnnotatedTypeMirror> getBounds(AnnotatedTypeMirror type) { AnnotatedTypeMirror bound = null; if (type.getKind() == TypeKind.TYPEVAR) { bound = ((AnnotatedTypeVariable)type).getUpperBound(); } else if (type.getKind() == TypeKind.WILDCARD) { AnnotatedWildcardType wt = (AnnotatedWildcardType)type; if (wt.getExtendsBound() == null) bound = wt.getExtendsBound(); else bound = wt.getSuperBound(); } else throw new IllegalArgumentException("type has no bounds: " + type.getKind()); if (bound == null) return Collections.emptyList(); else if (!TypesUtils.isAnonymousType(bound.getUnderlyingType())) return Collections.singletonList(bound); else return Collections.unmodifiableList(bound.directSuperTypes()); } }