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;
021:
022: import java.util.Collection;
023: import java.util.HashMap;
024: import java.util.Map;
025:
026: import org.apache.bcel.classfile.JavaClass;
027: import org.apache.bcel.classfile.Method;
028:
029: import edu.umd.cs.findbugs.FieldAnnotation;
030: import edu.umd.cs.findbugs.MethodAnnotation;
031: import edu.umd.cs.findbugs.annotations.CheckForNull;
032: import edu.umd.cs.findbugs.classfile.analysis.MethodInfo;
033: import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
034: import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
035: import edu.umd.cs.findbugs.util.ClassName;
036: import edu.umd.cs.findbugs.util.MapCache;
037:
038: /**
039: * Factory for creating ClassDescriptors, MethodDescriptors, and FieldDescriptors.
040: *
041: * @author David Hovemeyer
042: */
043: public class DescriptorFactory {
044: private static ThreadLocal<DescriptorFactory> instance = new ThreadLocal<DescriptorFactory>() {
045: @Override
046: protected DescriptorFactory initialValue() {
047: return new DescriptorFactory();
048: }
049: };
050:
051: private Map<String, ClassDescriptor> classDescriptorMap;
052: private Map<MethodDescriptor, MethodDescriptor> methodDescriptorMap;
053: private Map<FieldDescriptor, FieldDescriptor> fieldDescriptorMap;
054:
055: private DescriptorFactory() {
056: this .classDescriptorMap = new HashMap<String, ClassDescriptor>();
057: this .methodDescriptorMap = new HashMap<MethodDescriptor, MethodDescriptor>();
058: this .fieldDescriptorMap = new HashMap<FieldDescriptor, FieldDescriptor>();
059: }
060:
061: MapCache<String, String> stringCache = new MapCache<String, String>(
062: 10000);
063:
064: public static String canonicalizeString(String s) {
065: if (s == null)
066: return null;
067: DescriptorFactory instance = instance();
068: String result = instance.stringCache.get(s);
069: if (result != null)
070: return result;
071: instance.stringCache.put(s, s);
072: return s;
073: }
074:
075: /**
076: * Get the singleton instance of the DescriptorFactory.
077: *
078: * @return the singleton instance of the DescriptorFactory
079: */
080: public static DescriptorFactory instance() {
081: return instance.get();
082: }
083:
084: public static void clearInstance() {
085: instance.remove();
086: }
087:
088: public Collection<ClassDescriptor> getAllClassDescriptors() {
089: return classDescriptorMap.values();
090: }
091:
092: public void purge(Collection<ClassDescriptor> unusable) {
093: for (ClassDescriptor c : unusable)
094: classDescriptorMap.remove(c.getClassName());
095: }
096:
097: /**
098: * Get a ClassDescriptor for a class name in VM (slashed) format.
099: *
100: * @param className a class name in VM (slashed) format
101: * @return ClassDescriptor for that class
102: */
103: public ClassDescriptor getClassDescriptor(@SlashedClassName
104: String className) {
105: assert className.indexOf('.') == -1;
106: className = canonicalizeString(className);
107: ClassDescriptor classDescriptor = classDescriptorMap
108: .get(className);
109: if (classDescriptor == null) {
110: classDescriptor = new ClassDescriptor(className);
111: classDescriptorMap.put(className, classDescriptor);
112: }
113: return classDescriptor;
114: }
115:
116: /**
117: * Get a ClassDescriptor for a class name in dotted format.
118: *
119: * @param className a class name in dotted format
120: * @return ClassDescriptor for that class
121: */
122: public ClassDescriptor getClassDescriptorForDottedClassName(
123: @DottedClassName
124: String dottedClassName) {
125: return getClassDescriptor(dottedClassName.replace('.', '/'));
126: }
127:
128: public MethodDescriptor getMethodDescriptor(JavaClass jClass,
129: Method method) {
130: return getMethodDescriptor(ClassName.toSlashedClassName(jClass
131: .getClassName()), method.getName(), method
132: .getSignature(), method.isStatic());
133: }
134:
135: /**
136: * Get a MethodDescriptor.
137: *
138: * @param className name of the class containing the method, in VM format (e.g., "java/lang/String")
139: * @param methodName name of the method
140: * @param methodSignature signature of the method
141: * @param isStatic true if method is static, false otherwise
142: * @return MethodDescriptor
143: */
144: public MethodDescriptor getMethodDescriptor(@SlashedClassName
145: String className, String name, String signature, boolean isStatic) {
146: if (className == null)
147: throw new NullPointerException("className must be nonnull");
148: MethodDescriptor methodDescriptor = new MethodDescriptor(
149: className, name, signature, isStatic);
150: MethodDescriptor existing = methodDescriptorMap
151: .get(methodDescriptor);
152: if (existing == null) {
153: methodDescriptorMap.put(methodDescriptor, methodDescriptor);
154: existing = methodDescriptor;
155: }
156: return existing;
157: }
158:
159: public void profile() {
160: int total = 0;
161: int keys = 0;
162: int values = 0;
163: int bad = 0;
164: for (Map.Entry<MethodDescriptor, MethodDescriptor> e : methodDescriptorMap
165: .entrySet()) {
166: total++;
167: if (e.getKey() instanceof MethodInfo)
168: keys++;
169: else if (total - keys < 10)
170: System.out.println(e.getKey());
171: if (e.getValue() instanceof MethodInfo)
172: values++;
173: }
174: System.out.printf("Descriptor factory: %d/%d/%d\n", keys,
175: values, total);
176:
177: }
178:
179: public void canonicalize(MethodDescriptor m) {
180: MethodDescriptor existing = methodDescriptorMap.get(m);
181: if (m != existing) {
182: methodDescriptorMap.remove(m);
183: methodDescriptorMap.put(m, m);
184: }
185:
186: }
187:
188: public void canonicalize(FieldDescriptor m) {
189: FieldDescriptor existing = fieldDescriptorMap.get(m);
190: if (m != existing) {
191: fieldDescriptorMap.remove(m);
192: fieldDescriptorMap.put(m, m);
193: }
194:
195: }
196:
197: public MethodDescriptor getMethodDescriptor(MethodAnnotation ma) {
198: return getMethodDescriptor(ClassName.toSlashedClassName(ma
199: .getClassName()), ma.getMethodName(), ma
200: .getMethodSignature(), ma.isStatic());
201: }
202:
203: /**
204: * Get a FieldDescriptor.
205: *
206: * @param className the name of the class the field belongs to, in VM format (e.g., "java/lang/String")
207: * @param fieldName the name of the field
208: * @param fieldSignature the field signature (type)
209: * @param isStatic true if field is static, false if not
210: * @return FieldDescriptor
211: */
212: public FieldDescriptor getFieldDescriptor(@SlashedClassName
213: String className, String name, String signature, boolean isStatic) {
214: FieldDescriptor fieldDescriptor = new FieldDescriptor(
215: className, name, signature, isStatic);
216: FieldDescriptor existing = fieldDescriptorMap
217: .get(fieldDescriptor);
218: if (existing == null) {
219: fieldDescriptorMap.put(fieldDescriptor, fieldDescriptor);
220: existing = fieldDescriptor;
221: }
222: return existing;
223: }
224:
225: public FieldDescriptor getFieldDescriptor(FieldAnnotation ma) {
226: return getFieldDescriptor(ClassName.toSlashedClassName(ma
227: .getClassName()), ma.getFieldName(), ma
228: .getFieldSignature(), ma.isStatic());
229: }
230:
231: public static ClassDescriptor createClassDescriptor(JavaClass c) {
232: return DescriptorFactory
233: .createClassDescriptorFromDottedClassName(c
234: .getClassName());
235: }
236:
237: /**
238: * Create a class descriptor from a resource name.
239: *
240: * @param resourceName the resource name
241: * @return the class descriptor
242: */
243: public static ClassDescriptor createClassDescriptorFromResourceName(
244: String resourceName) {
245: if (!isClassResource(resourceName)) {
246: throw new IllegalArgumentException("Resource "
247: + resourceName + " is not a class");
248: }
249: return createClassDescriptor(resourceName.substring(0,
250: resourceName.length() - 6));
251: }
252:
253: /**
254: * Create a class descriptor from a field signature
255: *
256: */
257: public static @CheckForNull
258: ClassDescriptor createClassDescriptorFromFieldSignature(
259: String signature) {
260: int start = signature.indexOf('L');
261: if (start < 0) {
262: return null;
263: }
264: int end = signature.indexOf(';', start);
265: if (end < 0) {
266: return null;
267: }
268: return createClassDescriptor(signature
269: .substring(start + 1, end));
270: }
271:
272: /**
273: * Determine whether or not the given resource name refers to a class.
274: *
275: * @param resourceName the resource name
276: * @return true if the resource is a class, false otherwise
277: */
278: public static boolean isClassResource(String resourceName) {
279: // This could be more sophisticated.
280: return resourceName.endsWith(".class");
281: }
282:
283: public static ClassDescriptor createClassDescriptorFromSignature(
284: String signature) {
285: int first = 0;
286: while (signature.charAt(first) == '[')
287: first++;
288: signature = signature.substring(first);
289: if (signature.endsWith(";"))
290: signature = signature.substring(1, signature.length() - 1);
291: return createClassDescriptor(signature);
292: }
293:
294: public static ClassDescriptor createClassDescriptor(
295: @SlashedClassName
296: String className) {
297: className = ClassName.fromSignature(className);
298: return instance().getClassDescriptor(className);
299: }
300:
301: public static ClassDescriptor[] createClassDescriptor(
302: String[] classNames) {
303: ClassDescriptor[] result = new ClassDescriptor[classNames.length];
304: for (int i = 0; i < classNames.length; i++)
305: result[i] = createClassDescriptor(classNames[i]);
306: return result;
307: }
308:
309: public static ClassDescriptor createClassDescriptorFromDottedClassName(
310: String dottedClassName) {
311: return createClassDescriptor(dottedClassName.replace('.', '/'));
312: }
313: }
|