/* * FindBugs - Find Bugs in Java programs * Copyright (C) 2003-2007 University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.umd.cs.findbugs.ba.jsr305; import java.lang.annotation.ElementType; import java.util.Collection; import java.util.LinkedList; import edu.umd.cs.findbugs.ba.AnalysisContext; import edu.umd.cs.findbugs.ba.XClass; import edu.umd.cs.findbugs.classfile.CheckedAnalysisException; import edu.umd.cs.findbugs.classfile.ClassDescriptor; import edu.umd.cs.findbugs.classfile.DescriptorFactory; import edu.umd.cs.findbugs.classfile.Global; import edu.umd.cs.findbugs.classfile.MissingClassException; import edu.umd.cs.findbugs.classfile.analysis.AnnotationValue; import edu.umd.cs.findbugs.classfile.analysis.EnumValue; /** * Resolve annotations into type qualifiers. * * @author William Pugh */ public class TypeQualifierResolver { static ClassDescriptor typeQualifier = DescriptorFactory.createClassDescriptor(javax.annotation.meta.TypeQualifier.class); static ClassDescriptor typeQualifierNickname = DescriptorFactory .createClassDescriptor(javax.annotation.meta.TypeQualifierNickname.class); static ClassDescriptor typeQualifierDefault = DescriptorFactory .createClassDescriptor(javax.annotation.meta.TypeQualifierDefault.class); static ClassDescriptor elementTypeDescriptor = DescriptorFactory .createClassDescriptor(java.lang.annotation.ElementType.class); static ClassDescriptor googleNullable = DescriptorFactory.createClassDescriptor("com/google/common/base/Nullable"); /** * Resolve an AnnotationValue into a list of AnnotationValues * representing type qualifier annotations. * * @param value AnnotationValue representing the use of an annotation * @return Collection of AnnotationValues representing resolved * TypeQualifier annotations */ public static Collection<AnnotationValue> resolveTypeQualifiers(AnnotationValue value) { LinkedList<AnnotationValue> result = new LinkedList<AnnotationValue>(); resolveTypeQualifierNicknames(value, result, new LinkedList<ClassDescriptor>()); return result; } /** * Resolve collection of AnnotationValues (which have been used to * annotate an AnnotatedObject or method parameter) * into collection of resolved type qualifier AnnotationValues. * * @param values Collection of AnnotationValues used to annotate an AnnotatedObject or method parameter * @return Collection of resolved type qualifier AnnotationValues */ public static Collection<AnnotationValue> resolveTypeQualifierDefaults(Collection<AnnotationValue> values, ElementType elementType) { LinkedList<AnnotationValue> result = new LinkedList<AnnotationValue>(); for(AnnotationValue value : values) resolveTypeQualifierDefaults(value, elementType, result); return result; } /** * Resolve an annotation into AnnotationValues representing any type qualifier(s) * the annotation resolves to. Detects annotations which are directly * marked as TypeQualifier annotations, and also resolves the use of TypeQualifierNickname * annotations. * * @param value AnnotationValue representing the use of an annotation * @param result LinkedList containing resolved type qualifier AnnotationValues * @param onStack stack of annotations being processed; used to detect cycles in type qualifier nicknames */ private static void resolveTypeQualifierNicknames(AnnotationValue value, LinkedList<AnnotationValue> result, LinkedList<ClassDescriptor> onStack) { ClassDescriptor annotationClass = value.getAnnotationClass(); if (onStack.contains(annotationClass)) { AnalysisContext.logError("Cycle found in type nicknames: " + onStack); return; } try { onStack.add(annotationClass); try { if (annotationClass.equals(googleNullable)) { resolveTypeQualifierNicknames(new AnnotationValue(JSR305NullnessAnnotations.NULLABLE), result, onStack); return; } XClass c = Global.getAnalysisCache().getClassAnalysis(XClass.class, annotationClass); if (c.getAnnotationDescriptors().contains(typeQualifierNickname)) { for (ClassDescriptor d : c.getAnnotationDescriptors()) if (!c.equals(typeQualifierNickname)) resolveTypeQualifierNicknames(c.getAnnotation(d), result, onStack); } else if (c.getAnnotationDescriptors().contains(typeQualifier)) { result.add(value); } } catch (MissingClassException e) { logMissingAnnotationClass(e); return; } catch (CheckedAnalysisException e) { AnalysisContext.logError("Error resolving " + annotationClass, e); return; } } finally { onStack.removeLast(); } } public static void logMissingAnnotationClass(MissingClassException e) { ClassDescriptor c = e.getClassDescriptor(); if (c.getClassName().startsWith("javax.annotation")) AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(c); } /** * Resolve collection of AnnotationValues (which have been used to * annotate an AnnotatedObject or method parameter) * into collection of resolved type qualifier AnnotationValues. * * @param values Collection of AnnotationValues used to annotate an AnnotatedObject or method parameter * @return Collection of resolved type qualifier AnnotationValues */ public static Collection<AnnotationValue> resolveTypeQualifiers(Collection<AnnotationValue> values) { LinkedList<AnnotationValue> result = new LinkedList<AnnotationValue>(); LinkedList<ClassDescriptor> onStack = new LinkedList<ClassDescriptor>(); for(AnnotationValue value : values) resolveTypeQualifierNicknames(value, result, onStack); return result; } /** * Resolve an annotation into AnnotationValues representing any type qualifier(s) * the annotation resolves to. Detects annotations which are directly * marked as TypeQualifier annotations, and also resolves the use of TypeQualifierNickname * annotations. * * @param value AnnotationValue representing the use of an annotation * @param result LinkedList containing resolved type qualifier AnnotationValues * @param onStack stack of annotations being processed; used to detect cycles in type qualifier nicknames */ private static void resolveTypeQualifierDefaults(AnnotationValue value, ElementType defaultFor, LinkedList<AnnotationValue> result) { try { XClass c = Global.getAnalysisCache().getClassAnalysis(XClass.class, value.getAnnotationClass()); AnnotationValue defaultAnnotation = c.getAnnotation(typeQualifierDefault); if (defaultAnnotation == null) return; for(Object o : (Object[]) defaultAnnotation.getValue("value")) if (o instanceof EnumValue) { EnumValue e = (EnumValue) o; if (e.desc.equals(elementTypeDescriptor) && e.value.equals(defaultFor.name())) { for (ClassDescriptor d : c.getAnnotationDescriptors()) if (!d.equals(typeQualifierDefault)) resolveTypeQualifierNicknames(c.getAnnotation(d), result, new LinkedList<ClassDescriptor>()); break; } } } catch (MissingClassException e) { logMissingAnnotationClass(e); } catch (CheckedAnalysisException e) { AnalysisContext.logError("Error resolving " + value.getAnnotationClass(), e); } catch (ClassCastException e) { AnalysisContext.logError("ClassCastException " + value.getAnnotationClass(), e); } } }