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.LinkedList;
025:
026: import edu.umd.cs.findbugs.ba.AnalysisContext;
027: import edu.umd.cs.findbugs.ba.XClass;
028: import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
029: import edu.umd.cs.findbugs.classfile.ClassDescriptor;
030: import edu.umd.cs.findbugs.classfile.DescriptorFactory;
031: import edu.umd.cs.findbugs.classfile.Global;
032: import edu.umd.cs.findbugs.classfile.MissingClassException;
033: import edu.umd.cs.findbugs.classfile.analysis.AnnotationValue;
034: import edu.umd.cs.findbugs.classfile.analysis.EnumValue;
035:
036: /**
037: * Resolve annotations into type qualifiers.
038: *
039: * @author William Pugh
040: */
041: public class TypeQualifierResolver {
042: static ClassDescriptor typeQualifier = DescriptorFactory
043: .createClassDescriptor("javax/annotation/meta/TypeQualifier");
044: static ClassDescriptor typeQualifierNickname = DescriptorFactory
045: .createClassDescriptor("javax/annotation/meta/TypeQualifierNickname");
046: static ClassDescriptor typeQualifierDefault = DescriptorFactory
047: .createClassDescriptor("javax/annotation/meta/TypeQualifierDefault");
048: static ClassDescriptor elementTypeDescriptor = DescriptorFactory
049: .createClassDescriptor("java/lang/annotation/ElementType");
050:
051: /**
052: * Resolve an AnnotationValue into a list of AnnotationValues
053: * representing type qualifier annotations.
054: *
055: * @param value AnnotationValue representing the use of an annotation
056: * @return Collection of AnnotationValues representing resolved
057: * TypeQualifier annotations
058: */
059: public static Collection<AnnotationValue> resolveTypeQualifiers(
060: AnnotationValue value) {
061: LinkedList<AnnotationValue> result = new LinkedList<AnnotationValue>();
062: resolveTypeQualifierNicknames(value, result,
063: new LinkedList<ClassDescriptor>());
064: return result;
065: }
066:
067: /**
068: * Resolve collection of AnnotationValues (which have been used to
069: * annotate an AnnotatedObject or method parameter)
070: * into collection of resolved type qualifier AnnotationValues.
071: *
072: * @param values Collection of AnnotationValues used to annotate an AnnotatedObject or method parameter
073: * @return Collection of resolved type qualifier AnnotationValues
074: */
075: public static Collection<AnnotationValue> resolveTypeQualifierDefaults(
076: Collection<AnnotationValue> values, ElementType elementType) {
077: LinkedList<AnnotationValue> result = new LinkedList<AnnotationValue>();
078: for (AnnotationValue value : values)
079: resolveTypeQualifierDefaults(value, elementType, result);
080: return result;
081: }
082:
083: /**
084: * Resolve an annotation into AnnotationValues representing any type qualifier(s)
085: * the annotation resolves to. Detects annotations which are directly
086: * marked as TypeQualifier annotations, and also resolves the use of TypeQualifierNickname
087: * annotations.
088: *
089: * @param value AnnotationValue representing the use of an annotation
090: * @param result LinkedList containing resolved type qualifier AnnotationValues
091: * @param onStack stack of annotations being processed; used to detect cycles in type qualifier nicknames
092: */
093: private static void resolveTypeQualifierNicknames(
094: AnnotationValue value, LinkedList<AnnotationValue> result,
095: LinkedList<ClassDescriptor> onStack) {
096: if (onStack.contains(value.getAnnotationClass())) {
097: AnalysisContext.logError("Cycle found in type nicknames: "
098: + onStack);
099: return;
100: }
101: try {
102: onStack.add(value.getAnnotationClass());
103:
104: try {
105: XClass c = Global.getAnalysisCache().getClassAnalysis(
106: XClass.class, value.getAnnotationClass());
107: if (c.getAnnotationDescriptors().contains(
108: typeQualifierNickname)) {
109: for (ClassDescriptor d : c
110: .getAnnotationDescriptors())
111: if (!c.equals(typeQualifierNickname))
112: resolveTypeQualifierNicknames(c
113: .getAnnotation(d), result, onStack);
114: } else if (c.getAnnotationDescriptors().contains(
115: typeQualifier)) {
116: result.add(value);
117: }
118: } catch (MissingClassException e) {
119: // Hmm....this is a tough call.
120: // We probably don't want to bug users about
121: // annotations that are used in their code but aren't available
122: // at analysis time.
123: // AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e.getClassDescriptor());
124: return;
125: } catch (CheckedAnalysisException e) {
126: AnalysisContext.logError("Error resolving "
127: + value.getAnnotationClass(), e);
128: return;
129: }
130:
131: } finally {
132: onStack.removeLast();
133: }
134:
135: }
136:
137: /**
138: * Resolve collection of AnnotationValues (which have been used to
139: * annotate an AnnotatedObject or method parameter)
140: * into collection of resolved type qualifier AnnotationValues.
141: *
142: * @param values Collection of AnnotationValues used to annotate an AnnotatedObject or method parameter
143: * @return Collection of resolved type qualifier AnnotationValues
144: */
145: public static Collection<AnnotationValue> resolveTypeQualifiers(
146: Collection<AnnotationValue> values) {
147: LinkedList<AnnotationValue> result = new LinkedList<AnnotationValue>();
148: LinkedList<ClassDescriptor> onStack = new LinkedList<ClassDescriptor>();
149: for (AnnotationValue value : values)
150: resolveTypeQualifierNicknames(value, result, onStack);
151: return result;
152: }
153:
154: /**
155: * Resolve an annotation into AnnotationValues representing any type qualifier(s)
156: * the annotation resolves to. Detects annotations which are directly
157: * marked as TypeQualifier annotations, and also resolves the use of TypeQualifierNickname
158: * annotations.
159: *
160: * @param value AnnotationValue representing the use of an annotation
161: * @param result LinkedList containing resolved type qualifier AnnotationValues
162: * @param onStack stack of annotations being processed; used to detect cycles in type qualifier nicknames
163: */
164: private static void resolveTypeQualifierDefaults(
165: AnnotationValue value, ElementType defaultFor,
166: LinkedList<AnnotationValue> result) {
167:
168: try {
169: XClass c = Global.getAnalysisCache().getClassAnalysis(
170: XClass.class, value.getAnnotationClass());
171: AnnotationValue defaultAnnotation = c
172: .getAnnotation(typeQualifierDefault);
173: if (defaultAnnotation == null)
174: return;
175: for (Object o : (Object[]) defaultAnnotation
176: .getValue("value"))
177: if (o instanceof EnumValue) {
178: EnumValue e = (EnumValue) o;
179: if (e.desc.equals(elementTypeDescriptor)
180: && e.value.equals(defaultFor.name())) {
181: for (ClassDescriptor d : c
182: .getAnnotationDescriptors())
183: if (!d.equals(typeQualifierDefault))
184: resolveTypeQualifierNicknames(
185: c.getAnnotation(d),
186: result,
187: new LinkedList<ClassDescriptor>());
188: break;
189: }
190: }
191:
192: } catch (MissingClassException e) {
193: // Hmm....this is a tough call.
194: // We probably don't want to bug users about
195: // annotations that are used in their code but aren't available
196: // at analysis time.
197: // AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e.getClassDescriptor());
198: } catch (CheckedAnalysisException e) {
199: AnalysisContext.logError("Error resolving "
200: + value.getAnnotationClass(), e);
201:
202: } catch (ClassCastException e) {
203: AnalysisContext.logError("ClassCastException "
204: + value.getAnnotationClass(), e);
205:
206: }
207:
208: }
209:
210: }
|