edu.umd.cs.findbugs.classfile.DescriptorFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spotbugs Show documentation
Show all versions of spotbugs Show documentation
SpotBugs: Because it's easy!
/*
* 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.classfile;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ObjectType;
import edu.umd.cs.findbugs.FieldAnnotation;
import edu.umd.cs.findbugs.MethodAnnotation;
import edu.umd.cs.findbugs.classfile.analysis.MethodInfo;
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
import edu.umd.cs.findbugs.util.ClassName;
/**
* Factory for creating ClassDescriptors, MethodDescriptors, and
* FieldDescriptors.
*
* @author David Hovemeyer
*/
public class DescriptorFactory {
private static ThreadLocal instanceThreadLocal = new ThreadLocal() {
@Override
protected DescriptorFactory initialValue() {
return new DescriptorFactory();
}
};
private final Map classDescriptorMap;
private final Map dottedClassDescriptorMap;
private final Map methodDescriptorMap;
private final Map fieldDescriptorMap;
private static final ClassDescriptor MODULE_INFO = new ClassDescriptor("module-info");
private DescriptorFactory() {
this.classDescriptorMap = new HashMap<>();
this.dottedClassDescriptorMap = new HashMap<>();
this.methodDescriptorMap = new HashMap<>();
this.fieldDescriptorMap = new HashMap<>();
}
/**
* This method was designed to canonicalize String to improve performance,
* but now GC cost is cheaper than calculation cost in application thread
* so removing this old optimization makes SpotBugs 16% faster.
* @return given string instance
* @deprecated this hack is needless for modern JVM, at least Java8
*/
@Deprecated
public static String canonicalizeString(@CheckForNull String s) {
return s;
}
/**
* Get the singleton instance of the DescriptorFactory.
*
* @return the singleton instance of the DescriptorFactory
*/
public static DescriptorFactory instance() {
return instanceThreadLocal.get();
}
public static void clearInstance() {
instanceThreadLocal.remove();
}
public Collection getAllClassDescriptors() {
return classDescriptorMap.values();
}
public void purge(Collection unusable) {
for (ClassDescriptor c : unusable) {
classDescriptorMap.remove(c.getClassName());
dottedClassDescriptorMap.remove(ClassName.toDottedClassName(c.getClassName()));
}
}
public @Nonnull ClassDescriptor getClassDescriptor(Class> actualClass) {
return getClassDescriptorForDottedClassName(actualClass.getName());
}
/**
* Get a ClassDescriptor for a class name in VM (slashed) format.
*
* @param className
* a class name in VM (slashed) format
* @return ClassDescriptor for that class
*/
public @Nonnull ClassDescriptor getClassDescriptor(@SlashedClassName String className) {
assert className.indexOf('.') == -1;
ClassDescriptor classDescriptor = classDescriptorMap.get(className);
if (classDescriptor == null) {
if (MODULE_INFO.getClassName().equals(className)) {
// don't allow module info to be added to the map,
// which could be used to check referenced classes
return MODULE_INFO;
}
classDescriptor = new ClassDescriptor(className);
classDescriptorMap.put(className, classDescriptor);
}
return classDescriptor;
}
/**
* Get a ClassDescriptor for a class name in dotted format.
*
* @param dottedClassName
* a class name in dotted format
* @return ClassDescriptor for that class
*/
public ClassDescriptor getClassDescriptorForDottedClassName(@DottedClassName String dottedClassName) {
assert dottedClassName != null;
ClassDescriptor classDescriptor = dottedClassDescriptorMap.get(dottedClassName);
if (classDescriptor == null) {
classDescriptor = getClassDescriptor(ClassName.toSlashedClassName(dottedClassName));
dottedClassDescriptorMap.put(dottedClassName, classDescriptor);
}
return classDescriptor;
}
public MethodDescriptor getMethodDescriptor(JavaClass jClass, Method method) {
return getMethodDescriptor(ClassName.toSlashedClassName(jClass.getClassName()), method.getName(), method.getSignature(),
method.isStatic());
}
/**
* Get a MethodDescriptor.
*
* @param className
* name of the class containing the method, in VM format (e.g.,
* "java/lang/String")
* @param name
* name of the method
* @param signature
* signature of the method
* @param isStatic
* true if method is static, false otherwise
* @return MethodDescriptor
*/
public MethodDescriptor getMethodDescriptor(@SlashedClassName String className, String name, String signature,
boolean isStatic) {
if (className == null) {
throw new NullPointerException("className must be nonnull");
}
MethodDescriptor methodDescriptor = new MethodDescriptor(className, name, signature, isStatic);
MethodDescriptor existing = methodDescriptorMap.get(methodDescriptor);
if (existing == null) {
methodDescriptorMap.put(methodDescriptor, methodDescriptor);
existing = methodDescriptor;
}
return existing;
}
public void profile() {
int total = 0;
int keys = 0;
int values = 0;
int bad = 0;
for (Map.Entry e : methodDescriptorMap.entrySet()) {
total++;
if (e.getKey() instanceof MethodInfo) {
keys++;
}
if (e.getValue() instanceof MethodInfo) {
values++;
}
}
System.out.printf("Descriptor factory: %d/%d/%d%n", keys, values, total);
}
public void canonicalize(MethodDescriptor m) {
MethodDescriptor existing = methodDescriptorMap.get(m);
if (m != existing) {
methodDescriptorMap.put(m, m);
}
}
public void canonicalize(FieldDescriptor m) {
FieldDescriptor existing = fieldDescriptorMap.get(m);
if (m != existing) {
fieldDescriptorMap.put(m, m);
}
}
public MethodDescriptor getMethodDescriptor(MethodAnnotation ma) {
return getMethodDescriptor(ClassName.toSlashedClassName(ma.getClassName()), ma.getMethodName(), ma.getMethodSignature(),
ma.isStatic());
}
/**
* Get a FieldDescriptor.
*
* @param className
* the name of the class the field belongs to, in VM format
* (e.g., "java/lang/String")
* @param name
* the name of the field
* @param signature
* the field signature (type)
* @param isStatic
* true if field is static, false if not
* @return FieldDescriptor
*/
public FieldDescriptor getFieldDescriptor(@SlashedClassName String className, String name, String signature, boolean isStatic) {
FieldDescriptor fieldDescriptor = new FieldDescriptor(className, name, signature, isStatic);
FieldDescriptor existing = fieldDescriptorMap.get(fieldDescriptor);
if (existing == null) {
fieldDescriptorMap.put(fieldDescriptor, fieldDescriptor);
existing = fieldDescriptor;
}
return existing;
}
public FieldDescriptor getFieldDescriptor(@SlashedClassName String className, Field ma) {
return getFieldDescriptor(className, ma.getName(), ma.getSignature(), ma.isStatic());
}
public FieldDescriptor getFieldDescriptor(FieldAnnotation ma) {
return getFieldDescriptor(ClassName.toSlashedClassName(ma.getClassName()), ma.getFieldName(), ma.getFieldSignature(),
ma.isStatic());
}
/**
* Get a ClassDescriptor for the class described by given ObjectType object.
*
* @param type
* an ObjectType
* @return a ClassDescriptor for the class described by the ObjectType
*/
public static ClassDescriptor getClassDescriptor(ObjectType type) {
return instance().getClassDescriptorForDottedClassName(type.getClassName());
}
public static ClassDescriptor createClassDescriptor(JavaClass c) {
return DescriptorFactory.createClassDescriptorFromDottedClassName(c.getClassName());
}
/**
* Create a class descriptor from a resource name.
*
* @param resourceName
* the resource name
* @return the class descriptor
*/
public static ClassDescriptor createClassDescriptorFromResourceName(String resourceName) {
if (!isClassResource(resourceName)) {
throw new IllegalArgumentException("Resource " + resourceName + " is not a class");
}
return createClassDescriptor(resourceName.substring(0, resourceName.length() - 6));
}
/**
* Create a class descriptor from a field signature
*
*/
public static @CheckForNull ClassDescriptor createClassDescriptorFromFieldSignature(String signature) {
int start = signature.indexOf('L');
if (start < 0) {
return null;
}
int end = signature.indexOf(';', start);
if (end < 0) {
return null;
}
return createClassDescriptor(signature.substring(start + 1, end));
}
/**
* Determine whether or not the given resource name refers to a class.
*
* @param resourceName
* the resource name
* @return true if the resource is a class, false otherwise
*/
public static boolean isClassResource(String resourceName) {
// This could be more sophisticated.
return resourceName.endsWith(".class") && !isModuleInfo(resourceName);
}
/**
* Determine whether or not the given resource name refers to a module-info class.
*
* @param resourceName
* the resource name
* @return true if the resource is a module-info class, false otherwise
*/
public static boolean isModuleInfo(String resourceName) {
return resourceName.equals("module-info.class");
}
public static ClassDescriptor createClassDescriptorFromSignature(String signature) {
int length = signature.length();
if (length == 0) {
throw new IllegalArgumentException("Empty signature");
}
if (signature.charAt(0) == 'L' && signature.endsWith(";")) {
signature = signature.substring(1, signature.length() - 1);
}
return createClassDescriptor(signature);
}
public static ClassDescriptor createClassOrObjectDescriptorFromSignature(String signature) {
if (signature.charAt(0) == '[') {
return createClassDescriptor("java/lang/Object");
}
return createClassDescriptorFromSignature(signature);
}
public static ClassDescriptor createClassDescriptor(Class> aClass) {
return instance().getClassDescriptor(ClassName.toSlashedClassName(aClass.getName()));
}
public static @Nonnull ClassDescriptor createClassDescriptor(@SlashedClassName String className) {
return instance().getClassDescriptor(className);
}
public static ClassDescriptor[] createClassDescriptor(String[] classNames) {
if (classNames.length == 0) {
return ClassDescriptor.EMPTY_ARRAY;
}
ClassDescriptor[] result = new ClassDescriptor[classNames.length];
for (int i = 0; i < classNames.length; i++) {
result[i] = createClassDescriptor(classNames[i]);
}
return result;
}
public static ClassDescriptor createClassDescriptorFromDottedClassName(String dottedClassName) {
return createClassDescriptor(ClassName.toSlashedClassName(dottedClassName));
}
}