001: /*
002: * FindBugs - Find Bugs in Java programs
003: * Copyright (C) 2003-2007 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.ba.jsr305;
021:
022: import java.lang.annotation.ElementType;
023: import java.util.Collection;
024: import java.util.Collections;
025: import java.util.HashMap;
026: import java.util.HashSet;
027: import java.util.Map;
028: import java.util.Set;
029:
030: import javax.annotation.meta.When;
031:
032: import org.objectweb.asm.Opcodes;
033: import org.objectweb.asm.Type;
034:
035: import edu.umd.cs.findbugs.SystemProperties;
036: import edu.umd.cs.findbugs.annotations.CheckForNull;
037: import edu.umd.cs.findbugs.ba.AbstractClassMember;
038: import edu.umd.cs.findbugs.ba.AnalysisContext;
039: import edu.umd.cs.findbugs.ba.XMethod;
040: import edu.umd.cs.findbugs.classfile.ClassDescriptor;
041: import edu.umd.cs.findbugs.classfile.DescriptorFactory;
042: import edu.umd.cs.findbugs.classfile.analysis.AnnotatedObject;
043: import edu.umd.cs.findbugs.classfile.analysis.AnnotationValue;
044: import edu.umd.cs.findbugs.classfile.analysis.EnumValue;
045: import edu.umd.cs.findbugs.util.DualKeyHashMap;
046:
047: /**
048: * Figure out where and how type qualifier annotations are applied.
049: *
050: * @author William Pugh
051: * @author David Hovemeyer
052: */
053: public class TypeQualifierApplications {
054: static final boolean DEBUG = SystemProperties
055: .getBoolean("ctq.applications.debug");
056:
057: static class Data {
058: /** Type qualifier annotations applied directly to methods/fields/classes/etc. */
059: private Map<AnnotatedObject, Collection<AnnotationValue>> directObjectAnnotations = new HashMap<AnnotatedObject, Collection<AnnotationValue>>();
060:
061: /** Type qualifier annotations applied directly to method parameters. */
062: private DualKeyHashMap<XMethod, Integer, Collection<AnnotationValue>> directParameterAnnotations = new DualKeyHashMap<XMethod, Integer, Collection<AnnotationValue>>();
063:
064: /**
065: * Map of TypeQualifierValues to maps containing, for each AnnotatedObject,
066: * the effective TypeQualifierAnnotation (if any) for that AnnotatedObject.
067: */
068: private Map<TypeQualifierValue, Map<AnnotatedObject, TypeQualifierAnnotation>> effectiveObjectAnnotations = new HashMap<TypeQualifierValue, Map<AnnotatedObject, TypeQualifierAnnotation>>();
069:
070: /**
071: * Map of TypeQualifierValues to maps containing, for each XMethod/parameter,
072: * the effective TypeQualifierAnnotation (if any) for that XMethod/parameter.
073: */
074: private Map<TypeQualifierValue, DualKeyHashMap<XMethod, Integer, TypeQualifierAnnotation>> effectiveParameterAnnotations = new HashMap<TypeQualifierValue, DualKeyHashMap<XMethod, Integer, TypeQualifierAnnotation>>();
075: }
076:
077: static Data data = new Data();
078:
079: private static Map<TypeQualifierValue, DualKeyHashMap<XMethod, Integer, TypeQualifierAnnotation>> getEffectiveParameterAnnotations() {
080: return data.effectiveParameterAnnotations;
081: }
082:
083: private static Map<TypeQualifierValue, Map<AnnotatedObject, TypeQualifierAnnotation>> getEffectiveObjectAnnotations() {
084: return data.effectiveObjectAnnotations;
085: }
086:
087: private static DualKeyHashMap<XMethod, Integer, Collection<AnnotationValue>> getDirectParameterAnnotations() {
088: return data.directParameterAnnotations;
089: }
090:
091: private static Map<AnnotatedObject, Collection<AnnotationValue>> getDirectObjectAnnotations() {
092: return data.directObjectAnnotations;
093: }
094:
095: /**
096: * Get the direct annotations (if any) on given AnnotatedObject.
097: *
098: * @param m an AnnotatedObject
099: * @return Collection of AnnotationValues representing annotations directly
100: * applied to this AnnotatedObject
101: */
102: private static Collection<AnnotationValue> getDirectAnnotation(
103: AnnotatedObject m) {
104: Collection<AnnotationValue> result = getDirectObjectAnnotations()
105: .get(m);
106: if (result != null)
107: return result;
108: if (m.getAnnotationDescriptors().isEmpty())
109: return Collections.emptyList();
110: result = TypeQualifierResolver.resolveTypeQualifiers(m
111: .getAnnotations());
112: if (result.size() == 0)
113: result = Collections.emptyList();
114: getDirectObjectAnnotations().put(m, result);
115: return result;
116: }
117:
118: /**
119: * Get the direct annotations (if any) on given method parameter.
120: *
121: * @param m a method
122: * @param parameter a parameter (0 == first parameter)
123: * @return Collection of AnnotationValues representing annotations directly
124: * applied to this parameter
125: */
126: private static Collection<AnnotationValue> getDirectAnnotation(
127: XMethod m, int parameter) {
128: Collection<AnnotationValue> result = getDirectParameterAnnotations()
129: .get(m, parameter);
130: if (result != null)
131: return result;
132: if (m.getParameterAnnotationDescriptors(parameter).isEmpty())
133: return Collections.emptyList();
134: result = TypeQualifierResolver.resolveTypeQualifiers(m
135: .getParameterAnnotations(parameter));
136: if (result.size() == 0)
137: result = Collections.emptyList();
138: getDirectParameterAnnotations().put(m, parameter, result);
139: return result;
140: }
141:
142: /**
143: * Populate a Set of TypeQualifierAnnotations representing
144: * directly-applied type qualifier annotations on given
145: * method parameter.
146: *
147: * @param result Set of TypeQualifierAnnotations
148: * @param o a method
149: * @param parameter a parameter (0 == first parameter)
150: */
151: private static void getDirectApplications(
152: Set<TypeQualifierAnnotation> result, XMethod o,
153: int parameter) {
154: Collection<AnnotationValue> values = getDirectAnnotation(o,
155: parameter);
156: ElementType e = ElementType.PARAMETER;
157: for (AnnotationValue v : values) {
158: Object a = v.getValue("applyTo");
159: if (a instanceof Object[]) {
160: for (Object o2 : (Object[]) a)
161: if (o2 instanceof EnumValue) {
162: EnumValue ev = (EnumValue) o2;
163: if (ev.desc.getClassName().equals(
164: "java/lang/annotation/ElementType")
165: && e.toString().equals(ev.value))
166: constructTypeQualifierAnnotation(result, v);
167: }
168: } else
169: constructTypeQualifierAnnotation(result, v);
170: }
171: }
172:
173: /**
174: * Populate a Set of TypeQualifierAnnotations representing
175: * directly-applied type qualifier annotations on given
176: * AnnotatedObject.
177: *
178: * @param result Set of TypeQualifierAnnotations
179: * @param o an AnnotatedObject
180: * @param e ElementType representing kind of annotated object
181: */
182: private static void getDirectApplications(
183: Set<TypeQualifierAnnotation> result, AnnotatedObject o,
184: ElementType e) {
185: Collection<AnnotationValue> values = getDirectAnnotation(o);
186: for (AnnotationValue v : values) {
187: Object a = v.getValue("applyTo");
188: if (a instanceof Object[]) {
189: for (Object o2 : (Object[]) a)
190: if (o2 instanceof EnumValue) {
191: EnumValue ev = (EnumValue) o2;
192: if (ev.desc.getClassName().equals(
193: "java/lang/annotation/ElementType")
194: && e.toString().equals(ev.value))
195: constructTypeQualifierAnnotation(result, v);
196: }
197: } else if (o.getElementType().equals(e))
198: constructTypeQualifierAnnotation(result, v);
199: }
200: }
201:
202: /**
203: * Resolve a raw AnnotationValue into a TypeQualifierAnnotation.
204: *
205: * @param v a raw AnnotationValue
206: * @return a constructed TypeQualifierAnnotation
207: */
208: public static TypeQualifierAnnotation constructTypeQualifierAnnotation(
209: AnnotationValue v) {
210: assert v != null;
211: EnumValue whenValue = (EnumValue) v.getValue("when");
212: When when = whenValue == null ? When.ALWAYS : When
213: .valueOf(whenValue.value);
214: ClassDescriptor annotationClass = v.getAnnotationClass();
215: TypeQualifierValue tqv = TypeQualifierValue.getValue(
216: annotationClass, v.getValue("value"));
217: TypeQualifierAnnotation tqa = TypeQualifierAnnotation.getValue(
218: tqv, when);
219: return tqa;
220: }
221:
222: /**
223: * Resolve a raw AnnotationValue into a TypeQualifierAnnotation,
224: * storing result in given Set.
225: *
226: * @param set Set of resolved TypeQualifierAnnotations
227: * @param v a raw AnnotationValue
228: */
229: public static void constructTypeQualifierAnnotation(
230: Set<TypeQualifierAnnotation> set, AnnotationValue v) {
231: assert set != null;
232: TypeQualifierAnnotation tqa = constructTypeQualifierAnnotation(v);
233: set.add(tqa);
234: }
235:
236: /**
237: * Populate Set of TypeQualifierAnnotations
238: * for given AnnotatedObject,
239: * taking into account annotations
240: * applied to outer scopes (e.g., enclosing classes and packages.)
241: *
242: * @param result Set of TypeQualifierAnnotations
243: * @param o an AnnotatedObject
244: * @param e ElementType representing kind of AnnotatedObject
245: */
246: private static void getApplicableScopedApplications(
247: Set<TypeQualifierAnnotation> result, AnnotatedObject o,
248: ElementType e) {
249: AnnotatedObject outer = o.getContainingScope();
250: if (outer != null)
251: getApplicableScopedApplications(result, outer, e);
252: getDirectApplications(result, o, e);
253: }
254:
255: /**
256: * Get the collection of resolved TypeQualifierAnnotations for
257: * a given AnnotatedObject,
258: * taking into account annotations
259: * applied to outer scopes (e.g., enclosing classes and packages.)
260: *
261: * @param o an AnnotatedObject
262: * @param e ElementType representing kind of AnnotatedObject
263: * @return Collection of resolved TypeQualifierAnnotations
264: */
265: private static Collection<TypeQualifierAnnotation> getApplicableScopedApplications(
266: AnnotatedObject o, ElementType e) {
267: Set<TypeQualifierAnnotation> result = new HashSet<TypeQualifierAnnotation>();
268: getApplicableScopedApplications(result, o, e);
269: return result;
270: }
271:
272: /**
273: * Get the collection of resolved TypeQualifierAnnotations for
274: * a given parameter,
275: * taking into account annotations
276: * applied to outer scopes (e.g., enclosing classes and packages.)
277: *
278: * @param o a method
279: * @param parameter a parameter (0 == first parameter)
280: * @return Collection of resolved TypeQualifierAnnotations
281: */
282: private static Collection<TypeQualifierAnnotation> getApplicableScopedApplications(
283: XMethod o, int parameter) {
284: Set<TypeQualifierAnnotation> result = new HashSet<TypeQualifierAnnotation>();
285: ElementType e = ElementType.PARAMETER;
286: getApplicableScopedApplications(result, o, e);
287: getDirectApplications(result, o, parameter);
288: return result;
289: }
290:
291: /**
292: * Get the Collection of resolved TypeQualifierAnnotations representing
293: * directly applied and default (outer scope) type qualifier annotations
294: * for given AnnotatedObject.
295: *
296: * <p>NOTE: does not properly account for inherited annotations
297: * on instance methods.
298: * It is ok to call this method to find out generally-relevant TypeQualifierAnnotations,
299: * but not to find the effective TypeQualifierAnnotation.</p>
300: *
301: * @param o an AnnotatedObject
302: * @return Collection of TypeQualifierAnnotations applicable to the AnnotatedObject
303: */
304: public static Collection<TypeQualifierAnnotation> getApplicableApplications(
305: AnnotatedObject o) {
306: return getApplicableScopedApplications(o, o.getElementType());
307: }
308:
309: /**
310: * Get the Collection of resolved TypeQualifierAnnotations representing
311: * directly applied and default (outer scope) type qualifier annotations
312: * for given method parameter.
313: *
314: * <p>NOTE: does not properly account for inherited annotations
315: * on instance method parameters.
316: * It is ok to call this method to find out generally-relevant TypeQualifierAnnotations,
317: * but not to find the effective TypeQualifierAnnotation.</p>
318: *
319: * @param o a method
320: * @param parameter a parameter (0 == first parameter)
321: * @return Collection of TypeQualifierAnnotations applicable to the method parameter
322: */
323: public static Collection<TypeQualifierAnnotation> getApplicableApplications(
324: XMethod o, int parameter) {
325: return getApplicableScopedApplications(o, parameter);
326: }
327:
328: /**
329: * Look up a TypeQualifierAnnotation matching given TypeQualifierValue.
330: *
331: * @param typeQualifierAnnotations a Collection of TypeQualifierAnnotations
332: * @param typeQualifierValue a TypeQualifierValue
333: * @return matching TypeQualifierAnnotation, or null if none
334: */
335: private static @CheckForNull
336: TypeQualifierAnnotation findMatchingTypeQualifierAnnotation(
337: Collection<TypeQualifierAnnotation> typeQualifierAnnotations,
338: TypeQualifierValue typeQualifierValue) {
339: for (TypeQualifierAnnotation typeQualifierAnnotation : typeQualifierAnnotations) {
340: if (typeQualifierAnnotation.typeQualifier
341: .equals(typeQualifierValue)) {
342: return typeQualifierAnnotation;
343: }
344: }
345: return null;
346: }
347:
348: /**
349: * Check to see if one of the FindBugs-specific default annotation mechanisms
350: * is used on given AnnotatedObject to define a default value for
351: * given TypeQualifierValue.
352: *
353: * @param o an AnnotatedObject
354: * @param typeQualifierValue a TypeQualifierValue
355: * @param elementType type of annotated element
356: * @return default TypeQualifierAnnotation, or null if none
357: */
358: private static @CheckForNull
359: TypeQualifierAnnotation getFindBugsDefaultAnnotation(
360: AnnotatedObject o, TypeQualifierValue typeQualifierValue,
361: ElementType elementType) {
362: TypeQualifierAnnotation result;
363: Collection<AnnotationValue> values = TypeQualifierResolver
364: .resolveTypeQualifierDefaults(o.getAnnotations(),
365: elementType);
366: TypeQualifierAnnotation tqa = extractAnnotation(values,
367: typeQualifierValue);
368: if (tqa != null)
369: return tqa;
370:
371: if ((result = checkFindBugsDefaultAnnotation(
372: FindBugsDefaultAnnotations.DEFAULT_ANNOTATION, o,
373: typeQualifierValue)) != null) {
374: return result;
375: }
376:
377: switch (elementType) {
378: case FIELD:
379: result = checkFindBugsDefaultAnnotation(
380: FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_FIELDS,
381: o, typeQualifierValue);
382: break;
383: case METHOD:
384: result = checkFindBugsDefaultAnnotation(
385: FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_METHODS,
386: o, typeQualifierValue);
387: break;
388: case PARAMETER:
389: result = checkFindBugsDefaultAnnotation(
390: FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_PARAMETERS,
391: o, typeQualifierValue);
392: break;
393: default:
394: // ignore
395: }
396:
397: return result;
398: }
399:
400: private static @CheckForNull
401: TypeQualifierAnnotation checkFindBugsDefaultAnnotation(
402: ClassDescriptor defaultAnnotation, AnnotatedObject o,
403: TypeQualifierValue typeQualifierValue) {
404:
405: if (DEBUG) {
406: System.out.println("Checking for " + defaultAnnotation
407: + " containing " + typeQualifierValue + " on " + o);
408: }
409: // - check to see if default annotation is present; if not, return null
410: AnnotationValue annotationValue = o
411: .getAnnotation(defaultAnnotation);
412: if (annotationValue == null) {
413: if (DEBUG) {
414: System.out.println(" ===> no " + defaultAnnotation);
415: }
416: return null;
417: }
418:
419: // - get value - should be Type or array of Type
420: Object value = annotationValue.getValue("value");
421: if (value == null) {
422: if (DEBUG) {
423: System.out.println(" ===> value is null");
424: }
425: return null;
426: }
427: Object[] types;
428: if (value instanceof Object[]) {
429: types = (Object[]) value;
430: } else {
431: types = new Object[] { value };
432: }
433:
434: // - scan through array elements; see if any match the TypeQualifierValue (including type qualifier nicknames)
435: for (Object obj : types) {
436: if (!(obj instanceof Type)) {
437: if (DEBUG) {
438: System.out
439: .println("Found a non-Type value in value array of "
440: + defaultAnnotation.toString()
441: + " annotation");
442: }
443: continue;
444: }
445:
446: Type type = (Type) obj;
447: if (DEBUG) {
448: System.out.println(" ===> checking "
449: + type.getDescriptor());
450: }
451: if (type.getDescriptor().startsWith("[")) {
452: continue;
453: }
454: ClassDescriptor typeDesc = DescriptorFactory.instance()
455: .getClassDescriptor(type.getInternalName());
456:
457: // There is no general way to figure out whether a particular
458: // type is a type qualifier we're interested in without
459: // resolving it.
460: AnnotationValue annotation = new AnnotationValue(typeDesc);
461: Collection<AnnotationValue> resolvedTypeQualifiers = TypeQualifierResolver
462: .resolveTypeQualifiers(annotation);
463: TypeQualifierAnnotation tqa = extractAnnotation(
464: resolvedTypeQualifiers, typeQualifierValue);
465: if (tqa != null)
466: return tqa;
467:
468: }
469:
470: return null;
471: }
472:
473: private static TypeQualifierAnnotation extractAnnotation(
474: Collection<AnnotationValue> resolvedTypeQualifiers,
475: TypeQualifierValue typeQualifierValue) {
476: for (AnnotationValue typeQualifier : resolvedTypeQualifiers) {
477: TypeQualifierAnnotation tqa = constructTypeQualifierAnnotation(typeQualifier);
478: if (tqa.typeQualifier.equals(typeQualifierValue)) {
479: if (DEBUG) {
480: System.out.println(" ===> Found match " + tqa);
481: }
482: return tqa;
483: }
484: }
485: return null;
486: }
487:
488: /**
489: * Get the effective TypeQualifierAnnotation on given
490: * AnnotatedObject. Takes into account inherited and
491: * default (outer scope) annotations.
492: *
493: * @param o an AnnotatedObject
494: * @param typeQualifierValue a TypeQualifierValue specifying kind of annotation
495: * we want to look up
496: * @return the effective TypeQualifierAnnotation, or null if
497: * there is no effective TypeQualifierAnnotation on this
498: * AnnotatedObject
499: */
500: public static TypeQualifierAnnotation getEffectiveTypeQualifierAnnotation(
501: AnnotatedObject o, TypeQualifierValue typeQualifierValue) {
502: if (DEBUG) {
503: System.out.println("Looking up application of "
504: + typeQualifierValue + " on " + o);
505: }
506:
507: Map<AnnotatedObject, TypeQualifierAnnotation> map = getEffectiveObjectAnnotations()
508: .get(typeQualifierValue);
509: if (map == null) {
510: map = new HashMap<AnnotatedObject, TypeQualifierAnnotation>();
511: getEffectiveObjectAnnotations()
512: .put(typeQualifierValue, map);
513: }
514:
515: // Check cached answer
516: TypeQualifierAnnotation result;
517:
518: if (map.containsKey(o)) {
519: result = map.get(o);
520: } else {
521: // Compute answer
522:
523: TypeQualifierAnnotation tqa;
524:
525: // See if there is a direct application
526: tqa = getDirectTypeQualifierAnnotation(o,
527: typeQualifierValue);
528:
529: // If it's an instance method, check for an inherited annotation
530: if (tqa == null && (o instanceof XMethod)
531: && !((XMethod) o).isStatic()) {
532: tqa = getInheritedTypeQualifierAnnotation((XMethod) o,
533: typeQualifierValue);
534: }
535:
536: // Check for a default (outer scope) annotation
537: if (tqa == null) {
538: tqa = getDefaultTypeQualifierAnnotation(o,
539: typeQualifierValue);
540: }
541:
542: // Cache computed answer
543: result = tqa;
544: map.put(o, result);
545: }
546: if (DEBUG) {
547: System.out.println(" => Answer: " + result);
548: }
549:
550: // Return cached answer
551: return result;
552: }
553:
554: /**
555: * Get a directly-applied TypeQualifierAnnotation on given AnnotatedObject.
556: *
557: * @param o an AnnotatedObject
558: * @param typeQualifierValue the kind of TypeQualifierValue we are looking for
559: * @return directly-applied TypeQualifierAnnotation, or null if there is no
560: * such annotation on the AnnotatedObject
561: */
562: private static TypeQualifierAnnotation getDirectTypeQualifierAnnotation(
563: AnnotatedObject o, TypeQualifierValue typeQualifierValue) {
564: TypeQualifierAnnotation result;
565:
566: Set<TypeQualifierAnnotation> applications = new HashSet<TypeQualifierAnnotation>();
567: getDirectApplications(applications, o, o.getElementType());
568:
569: result = findMatchingTypeQualifierAnnotation(applications,
570: typeQualifierValue);
571:
572: return result;
573: }
574:
575: /**
576: * Get the effective inherited TypeQualifierAnnotation on given
577: * instance method.
578: *
579: * @param o an XMethod
580: * @param typeQualifierValue the kind of TypeQualifierValue we are looking for
581: * @return effective TypeQualifierAnnotation inherited from overridden supertype methods,
582: * or null if there is no inherited TypeQualifierAnnotation
583: */
584: private static TypeQualifierAnnotation getInheritedTypeQualifierAnnotation(
585: XMethod o, TypeQualifierValue typeQualifierValue) {
586: assert !o.isStatic();
587:
588: ReturnTypeAnnotationAccumulator accumulator = new ReturnTypeAnnotationAccumulator(
589: typeQualifierValue, o);
590: try {
591: AnalysisContext.currentAnalysisContext().getSubtypes2()
592: .traverseSupertypes(o.getClassDescriptor(),
593: accumulator);
594: return accumulator.getResult()
595: .getEffectiveTypeQualifierAnnotation();
596: } catch (ClassNotFoundException e) {
597: AnalysisContext.currentAnalysisContext()
598: .getLookupFailureCallback().reportMissingClass(e);
599: return null;
600: }
601: }
602:
603: /**
604: * Get the default (outer scope) annotation applicable to given
605: * AnnotatedObject.
606: *
607: * @param o an AnnotatedObject
608: * @param typeQualifierValue the kind of TypeQualifierValue we are looking for
609: * @return the applicable default TypeQualifierAnnotation, or null
610: * if there is no default TypeQualifierAnnotation
611: */
612: private static TypeQualifierAnnotation getDefaultTypeQualifierAnnotation(
613: AnnotatedObject o, TypeQualifierValue typeQualifierValue) {
614:
615: if (o instanceof AbstractClassMember
616: && (((AbstractClassMember) o).getAccessFlags() & Opcodes.ACC_SYNTHETIC) != 0)
617: return null; // synthetic methods don't get default annotations
618: TypeQualifierAnnotation result = null;
619:
620: ElementType elementType = o.getElementType();
621: while (o.getContainingScope() != null) {
622: o = o.getContainingScope();
623:
624: // Check direct applications of the type qualifier
625: Set<TypeQualifierAnnotation> applications = new HashSet<TypeQualifierAnnotation>();
626: getDirectApplications(applications, o, elementType);
627: result = findMatchingTypeQualifierAnnotation(applications,
628: typeQualifierValue);
629: if (result != null) {
630: // Great - found an outer scope with a relevant annotation
631: break;
632: }
633:
634: // Check FindBugs-specific default annotations
635: result = getFindBugsDefaultAnnotation(o,
636: typeQualifierValue, elementType);
637: if (result != null) {
638: break;
639: }
640: }
641: return result;
642: }
643:
644: /**
645: * Get the effective TypeQualifierAnnotation on given method parameter.
646: * Takes into account inherited and default (outer scope) annotations.
647: *
648: * @param xmethod a method
649: * @param parameter a parameter (0 == first parameter)
650: * @param typeQualifierValue the kind of TypeQualifierValue we are looking for
651: * @return effective TypeQualifierAnnotation on the parameter,
652: * or null if there is no effective TypeQualifierAnnotation
653: */
654: public static @CheckForNull
655: TypeQualifierAnnotation getEffectiveTypeQualifierAnnotation(
656: XMethod xmethod, int parameter,
657: TypeQualifierValue typeQualifierValue) {
658: if (DEBUG) {
659: System.out.println("Looking up application of "
660: + typeQualifierValue + " on " + xmethod
661: + " parameter " + parameter);
662: }
663:
664: DualKeyHashMap<XMethod, Integer, TypeQualifierAnnotation> map = getEffectiveParameterAnnotations()
665: .get(typeQualifierValue);
666: if (map == null) {
667: map = new DualKeyHashMap<XMethod, Integer, TypeQualifierAnnotation>();
668: getEffectiveParameterAnnotations().put(typeQualifierValue,
669: map);
670: }
671:
672: // Check cached answer
673: TypeQualifierAnnotation result;
674: if (map.containsKey(xmethod, parameter))
675: result = map.get(xmethod, parameter);
676: else {
677: // Compute answer
678: TypeQualifierAnnotation tqa;
679:
680: // Check direct application
681: tqa = getDirectTypeQualifierAnnotation(xmethod, parameter,
682: typeQualifierValue);
683:
684: // If it's an instance method, check for inherited annotation
685: if (tqa == null && !xmethod.isStatic()) {
686: tqa = getInheritedTypeQualifierAnnotation(xmethod,
687: parameter, typeQualifierValue);
688: }
689:
690: // Check for default (outer scope) annotation
691: if (tqa == null) {
692: tqa = getDefaultTypeQualifierAnnotation(xmethod,
693: parameter, typeQualifierValue);
694: }
695:
696: // Cache answer
697: result = tqa;
698: map.put(xmethod, parameter, result);
699: }
700:
701: if (DEBUG) {
702: System.out.println(" => Answer: " + result);
703: }
704:
705: // Return cached answer
706: return result;
707: }
708:
709: /**
710: * Get the TypeQualifierAnnotation directly applied to given
711: * method parameter.
712: *
713: * @param xmethod a method
714: * @param parameter a parameter (0 == first parameter)
715: * @param typeQualifierValue the kind of TypeQualifierValue we are looking for
716: * @return TypeQualifierAnnotation directly applied to the parameter,
717: * or null if there is no directly applied TypeQualifierAnnotation
718: */
719: private static @CheckForNull
720: TypeQualifierAnnotation getDirectTypeQualifierAnnotation(
721: XMethod xmethod, int parameter,
722: TypeQualifierValue typeQualifierValue) {
723: Set<TypeQualifierAnnotation> applications = new HashSet<TypeQualifierAnnotation>();
724: getDirectApplications(applications, xmethod, parameter);
725:
726: return findMatchingTypeQualifierAnnotation(applications,
727: typeQualifierValue);
728: }
729:
730: /**
731: * Get the effective inherited TypeQualifierAnnotation on the given
732: * instance method parameter.
733: *
734: * @param xmethod an instance method
735: * @param parameter a parameter (0 == first parameter)
736: * @param typeQualifierValue the kind of TypeQualifierValue we are looking for
737: * @return effective inherited TypeQualifierAnnotation on the parameter,
738: * or null if there is not effective TypeQualifierAnnotation
739: */
740: private static @CheckForNull
741: TypeQualifierAnnotation getInheritedTypeQualifierAnnotation(
742: XMethod xmethod, int parameter,
743: TypeQualifierValue typeQualifierValue) {
744: assert !xmethod.isStatic();
745:
746: ParameterAnnotationAccumulator accumulator = new ParameterAnnotationAccumulator(
747: typeQualifierValue, xmethod, parameter);
748: try {
749: AnalysisContext.currentAnalysisContext().getSubtypes2()
750: .traverseSupertypes(xmethod.getClassDescriptor(),
751: accumulator);
752: return accumulator.getResult()
753: .getEffectiveTypeQualifierAnnotation();
754: } catch (ClassNotFoundException e) {
755: AnalysisContext.currentAnalysisContext()
756: .getLookupFailureCallback().reportMissingClass(e);
757: return null;
758: }
759: }
760:
761: /**
762: * Get the default (outer-scope) TypeQualifierAnnotation on given method parameter.
763: *
764: * @param xmethod a method
765: * @param parameter a parameter (0 == first parameter)
766: * @param typeQualifierValue the kind of TypeQualifierValue we are looking for
767: * @return the default (outer scope) TypeQualifierAnnotation on the parameter,
768: * or null if there is no default TypeQualifierAnnotation
769: */
770: private static @CheckForNull
771: TypeQualifierAnnotation getDefaultTypeQualifierAnnotation(
772: XMethod xmethod, int parameter,
773: TypeQualifierValue typeQualifierValue) {
774:
775: if ((xmethod.getAccessFlags() & Opcodes.ACC_SYNTHETIC) != 0)
776: return null; // synthetic methods don't get default annotations
777: AnnotatedObject o = xmethod;
778:
779: while (o.getContainingScope() != null) {
780: o = o.getContainingScope();
781:
782: // Check for direct type qualifier annotation
783: Set<TypeQualifierAnnotation> applications = new HashSet<TypeQualifierAnnotation>();
784: getDirectApplications(applications, o,
785: ElementType.PARAMETER);
786: TypeQualifierAnnotation tqa = findMatchingTypeQualifierAnnotation(
787: applications, typeQualifierValue);
788: if (tqa != null) {
789: // Found matching annotation in outer scope
790: return tqa;
791: }
792:
793: // Check for FindBugs-specific default annotation
794: tqa = getFindBugsDefaultAnnotation(o, typeQualifierValue,
795: ElementType.PARAMETER);
796: if (tqa != null) {
797: return tqa;
798: }
799: }
800:
801: return null;
802: }
803: }
|