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.classfile.analysis;
021:
022: import java.lang.annotation.ElementType;
023: import java.util.Arrays;
024: import java.util.Collection;
025: import java.util.Collections;
026: import java.util.HashMap;
027: import java.util.IdentityHashMap;
028: import java.util.Map;
029:
030: import org.apache.bcel.Constants;
031: import org.objectweb.asm.Opcodes;
032:
033: import edu.umd.cs.findbugs.annotations.CheckForNull;
034: import edu.umd.cs.findbugs.annotations.Nullable;
035: import edu.umd.cs.findbugs.ba.SignatureParser;
036: import edu.umd.cs.findbugs.ba.XClass;
037: import edu.umd.cs.findbugs.ba.XFactory;
038: import edu.umd.cs.findbugs.ba.XMethod;
039: import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
040: import edu.umd.cs.findbugs.classfile.ClassDescriptor;
041: import edu.umd.cs.findbugs.classfile.DescriptorFactory;
042: import edu.umd.cs.findbugs.classfile.Global;
043: import edu.umd.cs.findbugs.classfile.MethodDescriptor;
044: import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
045: import edu.umd.cs.findbugs.util.Util;
046:
047: /**
048: * @author pugh
049: */
050: public class MethodInfo extends MethodDescriptor implements XMethod,
051: AnnotatedObject {
052:
053: static public class Builder {
054: final int accessFlags;
055:
056: final String className, methodName, methodSignature;
057:
058: String[] exceptions;
059: String methodSourceSignature;
060: boolean isUnconditionalThrower;
061:
062: final Map<ClassDescriptor, AnnotationValue> methodAnnotations = new HashMap<ClassDescriptor, AnnotationValue>();
063:
064: final Map<Integer, Map<ClassDescriptor, AnnotationValue>> methodParameterAnnotations = new HashMap<Integer, Map<ClassDescriptor, AnnotationValue>>();
065:
066: public Builder(@DottedClassName
067: String className, String methodName, String methodSignature,
068: int accessFlags) {
069: this .className = className;
070: this .methodName = methodName;
071: this .methodSignature = methodSignature;
072: this .accessFlags = accessFlags;
073: }
074:
075: public void setSourceSignature(String methodSourceSignature) {
076: this .methodSourceSignature = methodSourceSignature;
077: }
078:
079: public void setThrownExceptions(String[] exceptions) {
080: this .exceptions = exceptions;
081: }
082:
083: public void addAnnotation(String name, AnnotationValue value) {
084: ClassDescriptor annotationClass = DescriptorFactory
085: .createClassDescriptorFromSignature(name);
086: methodAnnotations.put(annotationClass, value);
087: }
088:
089: public void addParameterAnnotation(int parameter, String name,
090: AnnotationValue value) {
091: ClassDescriptor annotationClass = DescriptorFactory
092: .createClassDescriptorFromSignature(name);
093: Map<ClassDescriptor, AnnotationValue> map = methodParameterAnnotations
094: .get(parameter);
095: if (map == null) {
096: map = new HashMap<ClassDescriptor, AnnotationValue>();
097: methodParameterAnnotations.put(parameter, map);
098: }
099: map.put(annotationClass, value);
100: }
101:
102: public MethodInfo build() {
103: return new MethodInfo(className, methodName,
104: methodSignature, methodSourceSignature,
105: accessFlags, isUnconditionalThrower, exceptions,
106: methodAnnotations, methodParameterAnnotations);
107: }
108:
109: /**
110: *
111: */
112: public void setIsUnconditionalThrower() {
113: isUnconditionalThrower = true;
114:
115: }
116: }
117:
118: final int accessFlags;
119:
120: final String methodSourceSignature;
121:
122: final @CheckForNull
123: String[] exceptions;
124:
125: Map<ClassDescriptor, AnnotationValue> methodAnnotations;
126:
127: Map<Integer, Map<ClassDescriptor, AnnotationValue>> methodParameterAnnotations;
128:
129: static IdentityHashMap<MethodInfo, Void> unconditionalThrowers = new IdentityHashMap<MethodInfo, Void>();
130:
131: /**
132: * @param className
133: * @param methodName
134: * @param methodSignature
135: * @param methodSourceSignature
136: * @param isStatic
137: */
138: MethodInfo(
139: String className,
140: String methodName,
141: String methodSignature,
142: String methodSourceSignature,
143: int accessFlags,
144: boolean isUnconditionalThrower,
145: @CheckForNull
146: String[] exceptions,
147: Map<ClassDescriptor, AnnotationValue> methodAnnotations,
148: Map<Integer, Map<ClassDescriptor, AnnotationValue>> methodParameterAnnotations) {
149: super (className, methodName, methodSignature,
150: (accessFlags & Constants.ACC_STATIC) != 0);
151: this .accessFlags = accessFlags;
152: this .exceptions = exceptions;
153: if (exceptions != null)
154: for (int i = 0; i < exceptions.length; i++)
155: exceptions[i] = DescriptorFactory
156: .canonicalizeString(exceptions[i]);
157: this .methodSourceSignature = DescriptorFactory
158: .canonicalizeString(methodSourceSignature);
159: this .methodAnnotations = Util.immutableMap(methodAnnotations);
160: this .methodParameterAnnotations = Util
161: .immutableMap(methodParameterAnnotations);
162: if (isUnconditionalThrower)
163: unconditionalThrowers.put(this , null);
164: if (false && exceptions != null && exceptions.length > 0)
165: System.out.println(this + " throws "
166: + Arrays.asList(exceptions));
167: }
168:
169: public @CheckForNull
170: String[] getThrownExceptions() {
171: return exceptions;
172: }
173:
174: public boolean isUnconditionalThrower() {
175: return unconditionalThrowers.containsKey(this );
176: }
177:
178: public int getNumParams() {
179: return new SignatureParser(getSignature()).getNumParameters();
180: }
181:
182: private boolean checkFlag(int flag) {
183: return (accessFlags & flag) != 0;
184: }
185:
186: public boolean isNative() {
187: return checkFlag(Constants.ACC_NATIVE);
188: }
189:
190: public boolean isAbstract() {
191: return checkFlag(Constants.ACC_ABSTRACT);
192: }
193:
194: public boolean isSynchronized() {
195: return checkFlag(Constants.ACC_SYNCHRONIZED);
196: }
197:
198: /* (non-Javadoc)
199: * @see edu.umd.cs.findbugs.ba.XMethod#isReturnTypeReferenceType()
200: */
201: public boolean isReturnTypeReferenceType() {
202: SignatureParser parser = new SignatureParser(getSignature());
203: String returnTypeSig = parser.getReturnTypeSignature();
204: return SignatureParser.isReferenceType(returnTypeSig);
205: }
206:
207: public @DottedClassName
208: String getClassName() {
209: return getClassDescriptor().toDottedClassName();
210: }
211:
212: public @DottedClassName
213: String getPackageName() {
214: return getClassDescriptor().getPackageName();
215: }
216:
217: public String getSourceSignature() {
218: return methodSourceSignature;
219: }
220:
221: /*
222: * (non-Javadoc)
223: *
224: * @see java.lang.Comparable#compareTo(java.lang.Object)
225: */
226: public int compareTo(Object rhs) {
227: if (rhs instanceof MethodDescriptor) {
228: return super .compareTo((MethodDescriptor) rhs);
229: }
230:
231: if (rhs instanceof XMethod) {
232: return XFactory.compare((XMethod) this , (XMethod) rhs);
233: }
234:
235: throw new ClassCastException("Can't compare a "
236: + this .getClass().getName() + " to a "
237: + rhs.getClass().getName());
238: }
239:
240: /*
241: * (non-Javadoc)
242: *
243: * @see edu.umd.cs.findbugs.ba.AccessibleEntity#getAccessFlags()
244: */
245: public int getAccessFlags() {
246: return accessFlags;
247: }
248:
249: /*
250: * (non-Javadoc)
251: *
252: * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isFinal()
253: */
254: public boolean isFinal() {
255: return checkFlag(Constants.ACC_FINAL);
256: }
257:
258: /*
259: * (non-Javadoc)
260: *
261: * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isPrivate()
262: */
263: public boolean isPrivate() {
264: return checkFlag(Constants.ACC_PRIVATE);
265: }
266:
267: public boolean isDeprecated() {
268: return checkFlag(Opcodes.ACC_DEPRECATED);
269: }
270:
271: /*
272: * (non-Javadoc)
273: *
274: * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isProtected()
275: */
276: public boolean isProtected() {
277: return checkFlag(Constants.ACC_PROTECTED);
278: }
279:
280: /*
281: * (non-Javadoc)
282: *
283: * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isPublic()
284: */
285: public boolean isPublic() {
286: return checkFlag(Constants.ACC_PUBLIC);
287: }
288:
289: public boolean isSynthetic() {
290: return checkFlag(Constants.ACC_SYNTHETIC);
291: }
292:
293: /*
294: * (non-Javadoc)
295: *
296: * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isResolved()
297: */
298: public boolean isResolved() {
299: return true;
300: }
301:
302: public Collection<ClassDescriptor> getParameterAnnotationDescriptors(
303: int param) {
304: Map<ClassDescriptor, AnnotationValue> map = methodParameterAnnotations
305: .get(param);
306: if (map == null)
307: return Collections.emptySet();
308: return map.keySet();
309: }
310:
311: public @Nullable
312: AnnotationValue getParameterAnnotation(int param,
313: ClassDescriptor desc) {
314: Map<ClassDescriptor, AnnotationValue> map = methodParameterAnnotations
315: .get(param);
316: if (map == null)
317: return null;
318: return map.get(desc);
319: }
320:
321: public Collection<AnnotationValue> getParameterAnnotations(int param) {
322: Map<ClassDescriptor, AnnotationValue> map = methodParameterAnnotations
323: .get(param);
324: if (map == null)
325: return Collections.emptySet();
326: return map.values();
327: }
328:
329: public Collection<ClassDescriptor> getAnnotationDescriptors() {
330: return methodAnnotations.keySet();
331: }
332:
333: public AnnotationValue getAnnotation(ClassDescriptor desc) {
334: return methodAnnotations.get(desc);
335: }
336:
337: public Collection<AnnotationValue> getAnnotations() {
338: return methodAnnotations.values();
339: }
340:
341: /**
342: * Destructively add an annotation.
343: * We do this for "built-in" annotations that might not
344: * be directly evident in the code.
345: * It's not a great idea in general, but we can
346: * get away with it as long as it's done early
347: * enough (i.e., before anyone asks what annotations
348: * this method has.)
349: *
350: * @param annotationValue an AnnotationValue representing a method annotation
351: */
352: public void addAnnotation(AnnotationValue annotationValue) {
353: HashMap<ClassDescriptor, AnnotationValue> updatedAnnotations = new HashMap<ClassDescriptor, AnnotationValue>(
354: methodAnnotations);
355: updatedAnnotations.put(annotationValue.getAnnotationClass(),
356: annotationValue);
357: methodAnnotations = updatedAnnotations;
358: }
359:
360: /**
361: * Destructively add a parameter annotation.
362: *
363: * @param param parameter (0 == first parameter)
364: * @param annotationValue an AnnotationValue representing a parameter annotation
365: */
366: public void addParameterAnnotation(int param,
367: AnnotationValue annotationValue) {
368: HashMap<Integer, Map<ClassDescriptor, AnnotationValue>> updatedAnnotations = new HashMap<Integer, Map<ClassDescriptor, AnnotationValue>>(
369: methodParameterAnnotations);
370: Map<ClassDescriptor, AnnotationValue> paramMap = updatedAnnotations
371: .get(param);
372: if (paramMap == null) {
373: paramMap = new HashMap<ClassDescriptor, AnnotationValue>();
374: updatedAnnotations.put(param, paramMap);
375: }
376: paramMap.put(annotationValue.getAnnotationClass(),
377: annotationValue);
378:
379: methodParameterAnnotations = updatedAnnotations;
380: }
381:
382: /* (non-Javadoc)
383: * @see edu.umd.cs.findbugs.ba.XMethod#getMethodDescriptor()
384: */
385: public MethodDescriptor getMethodDescriptor() {
386: return this ;
387: }
388:
389: public ElementType getElementType() {
390: if (getName().equals("<init>"))
391: return ElementType.CONSTRUCTOR;
392: return ElementType.METHOD;
393: }
394:
395: public @CheckForNull
396: AnnotatedObject getContainingScope() {
397: try {
398: return (ClassInfo) Global.getAnalysisCache()
399: .getClassAnalysis(XClass.class,
400: getClassDescriptor());
401: } catch (CheckedAnalysisException e) {
402: return null;
403: }
404: }
405:
406: }
|