/*
* FindBugs - Find Bugs in Java programs
* Copyright (C) 2006, 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.analysis;
import java.lang.annotation.ElementType;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.CheckForNull;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.XClass;
import edu.umd.cs.findbugs.ba.XField;
import edu.umd.cs.findbugs.ba.XMethod;
import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.DescriptorFactory;
import edu.umd.cs.findbugs.classfile.FieldOrMethodDescriptor;
import edu.umd.cs.findbugs.classfile.Global;
import edu.umd.cs.findbugs.classfile.ICodeBaseEntry;
import edu.umd.cs.findbugs.classfile.MethodDescriptor;
import edu.umd.cs.findbugs.classfile.engine.SelfMethodCalls;
import edu.umd.cs.findbugs.util.MultiMap;
import edu.umd.cs.findbugs.util.TopologicalSort;
import edu.umd.cs.findbugs.util.TopologicalSort.OutEdges2;
import edu.umd.cs.findbugs.util.Util;
/**
* ClassInfo represents important metadata about a loaded class, such as its
* superclass, access flags, codebase entry, etc.
*
* @author David Hovemeyer
*/
public class ClassInfo extends ClassNameAndSuperclassInfo implements XClass, AnnotatedObject {
private final FieldInfo[] xFields;
private final MethodInfo[] xMethods;
private final MethodInfo[] methodsInCallOrder;
private final ClassDescriptor immediateEnclosingClass;
/* final */Map<ClassDescriptor, AnnotationValue> classAnnotations;
final private String classSourceSignature;
final private String source;
private final boolean usesConcurrency;
private final boolean hasStubs;
public static class Builder extends ClassNameAndSuperclassInfo.Builder {
private List<FieldInfo> fieldInfoList = new LinkedList<FieldInfo>();
private List<MethodInfo> methodInfoList = new LinkedList<MethodInfo>();
/**
* Mapping from one method signature to its bridge method signature
*/
private final Map<MethodInfo, String> bridgedSignatures = new IdentityHashMap<MethodInfo, String>();
private ClassDescriptor immediateEnclosingClass;
final Map<ClassDescriptor, AnnotationValue> classAnnotations = new HashMap<ClassDescriptor, AnnotationValue>(3);
private String classSourceSignature;
private String source;
boolean usesConcurrency;
boolean hasStubs;
@Override
public ClassInfo build() {
AnalysisContext context = AnalysisContext.currentAnalysisContext();
FieldInfo fields[];
MethodInfo methods[];
if (fieldInfoList.size() == 0)
fields = FieldInfo.EMPTY_ARRAY;
else
fields = fieldInfoList.toArray(new FieldInfo[fieldInfoList.size()]);
for (Map.Entry<MethodInfo, String> e : bridgedSignatures.entrySet()) {
MethodInfo method = e.getKey();
String signature = e.getValue();
for (MethodInfo m : methodInfoList)
if (m.getName().equals(method.getName()) && m.getSignature().equals(signature)) {
context.setBridgeMethod(method, m);
}
}
if (methodInfoList.size() == 0)
methods = MethodInfo.EMPTY_ARRAY;
else
methods = methodInfoList.toArray(new MethodInfo[methodInfoList.size()]);
return new ClassInfo(classDescriptor, classSourceSignature, superclassDescriptor, interfaceDescriptorList,
codeBaseEntry, accessFlags, source, majorVersion, minorVersion, referencedClassDescriptorList,
calledClassDescriptors, classAnnotations, fields, methods, immediateEnclosingClass, usesConcurrency,
hasStubs);
}
public void setSource(String source) {
this.source = source;
}
/**
* @return Returns the classDescriptor.
*/
public ClassDescriptor getClassDescriptor() {
return classDescriptor;
}
public void setSourceSignature(String classSourceSignature) {
this.classSourceSignature = classSourceSignature;
}
public void addAnnotation(String name, AnnotationValue value) {
ClassDescriptor annotationClass = DescriptorFactory.createClassDescriptorFromSignature(name);
classAnnotations.put(annotationClass, value);
}
/**
* @param fieldDescriptorList
* The fieldDescriptorList to set.
*/
public void setFieldDescriptorList(FieldInfo[] fieldDescriptorList) {
this.fieldInfoList = Arrays.asList(fieldDescriptorList);
}
public void addFieldDescriptor(FieldInfo field) {
fieldInfoList.add(field);
}
/**
* @param methodDescriptorList
* The methodDescriptorList to set.
*/
public void setMethodDescriptorList(MethodInfo[] methodDescriptorList) {
this.methodInfoList = Arrays.asList(methodDescriptorList);
}
public void addMethodDescriptor(MethodInfo method) {
methodInfoList.add(method);
}
public void addBridgeMethodDescriptor(MethodInfo method, String bridgedSignature) {
if (bridgedSignature != null) {
bridgedSignatures.put(method, bridgedSignature);
}
addMethodDescriptor(method);
}
/**
* @param immediateEnclosingClass
* The immediateEnclosingClass to set.
*/
public void setImmediateEnclosingClass(ClassDescriptor immediateEnclosingClass) {
this.immediateEnclosingClass = immediateEnclosingClass;
}
public void setUsesConcurrency() {
usesConcurrency = true;
}
public void setHasStubs() {
hasStubs = true;
}
}
private MethodInfo[] computeMethodsInCallOrder() {
final Map<String, MethodInfo> map = new HashMap<String, MethodInfo>();
for (MethodInfo m : xMethods) {
map.put(m.getName() + m.getSignature() + m.isStatic(), m);
}
final MultiMap<MethodInfo, MethodInfo> multiMap = SelfMethodCalls.getSelfCalls(getClassDescriptor(), map);
OutEdges2<MethodInfo> edges1 = new OutEdges2<MethodInfo>() {
public Collection<MethodInfo> getOutEdges(MethodInfo method) {
return multiMap.get(method);
}
public int score(MethodInfo e) {
return e.getMethodCallCount();
}
};
List<MethodInfo> result = TopologicalSort.sortByCallGraph(Arrays.asList(xMethods), edges1);
assert xMethods.length == result.size();
return result.toArray(new MethodInfo[result.size()]);
}
/**
*
* @param classDescriptor
* ClassDescriptor representing the class name
* @param superclassDescriptor
* ClassDescriptor representing the superclass name
* @param interfaceDescriptorList
* ClassDescriptors representing implemented interface names
* @param codeBaseEntry
* codebase entry class was loaded from
* @param accessFlags
* class's access flags
* @param referencedClassDescriptorList
* ClassDescriptors of all classes/interfaces referenced by the
* class
* @param calledClassDescriptors
* TODO
* @param fieldDescriptorList
* FieldDescriptors of fields defined in the class
* @param methodInfoList
* MethodDescriptors of methods defined in the class
* @param usesConcurrency
* TODO
* @param hasStubs
* TODO
*/
private ClassInfo(ClassDescriptor classDescriptor, String classSourceSignature, ClassDescriptor superclassDescriptor,
ClassDescriptor[] interfaceDescriptorList, ICodeBaseEntry codeBaseEntry, int accessFlags, String source,
int majorVersion, int minorVersion, Collection<ClassDescriptor> referencedClassDescriptorList,
Set<ClassDescriptor> calledClassDescriptors, Map<ClassDescriptor, AnnotationValue> classAnnotations,
FieldInfo[] fieldDescriptorList, MethodInfo[] methodInfoList, ClassDescriptor immediateEnclosingClass,
boolean usesConcurrency, boolean hasStubs) {
super(classDescriptor, superclassDescriptor, interfaceDescriptorList, codeBaseEntry, accessFlags,
referencedClassDescriptorList, calledClassDescriptors, majorVersion, minorVersion);
this.source = source;
this.classSourceSignature = classSourceSignature;
if (fieldDescriptorList.length == 0)
fieldDescriptorList = FieldInfo.EMPTY_ARRAY;
this.xFields = fieldDescriptorList;
this.xMethods = methodInfoList;
this.immediateEnclosingClass = immediateEnclosingClass;
this.classAnnotations = Util.immutableMap(classAnnotations);
this.usesConcurrency = usesConcurrency;
this.hasStubs = hasStubs;
this.methodsInCallOrder = computeMethodsInCallOrder();
if (false) {
System.out.println("Methods in call order for " + classDescriptor);
for (MethodInfo m : methodsInCallOrder) {
System.out.println(" " + m);
}
System.out.println();
}
}
/**
* @return Returns the fieldDescriptorList.
*/
public List<? extends XField> getXFields() {
return Arrays.asList(xFields);
}
/**
* @return Returns the methodDescriptorList.
*/
public List<? extends XMethod> getXMethods() {
return Arrays.asList(xMethods);
}
/**
* @return Returns the methodDescriptorList.
*/
public List<? extends XMethod> getXMethodsInCallOrder() {
return Arrays.asList(methodsInCallOrder);
}
/*
* (non-Javadoc)
*
* @see edu.umd.cs.findbugs.ba.XClass#findMethod(java.lang.String,
* java.lang.String, boolean)
*/
public XMethod findMethod(String methodName, String methodSig, boolean isStatic) {
int hash = FieldOrMethodDescriptor.getNameSigHashCode(methodName, methodSig);
for (MethodInfo mInfo : xMethods)
if (mInfo.getNameSigHashCode() == hash && mInfo.getName().equals(methodName)
&& mInfo.getSignature().equals(methodSig) && mInfo.isStatic() == isStatic)
return mInfo;
return null;
}
/*
* (non-Javadoc)
*
* @see
* edu.umd.cs.findbugs.ba.XClass#findMethod(edu.umd.cs.findbugs.classfile
* .MethodDescriptor)
*/
public XMethod findMethod(MethodDescriptor descriptor) {
if (!descriptor.getClassDescriptor().equals(this)) {
throw new IllegalArgumentException();
}
return findMatchingMethod(descriptor);
}
public XMethod findMatchingMethod(MethodDescriptor descriptor) {
return findMethod(descriptor.getName(), descriptor.getSignature(), descriptor.isStatic());
}
/*
* (non-Javadoc)
*
* @see edu.umd.cs.findbugs.ba.XClass#findField(java.lang.String,
* java.lang.String, boolean)
*/
public XField findField(String name, String signature, boolean isStatic) {
int hash = FieldOrMethodDescriptor.getNameSigHashCode(name, signature);
for (FieldInfo fInfo : xFields)
if (fInfo.getNameSigHashCode() == hash && fInfo.getName().equals(name) && fInfo.getSignature().equals(signature)
&& fInfo.isStatic() == isStatic)
return fInfo;
try {
if (getSuperclassDescriptor() == null)
return null;
XClass superClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, getSuperclassDescriptor());
XField result = superClass.findField(name, signature, isStatic);
if (result != null)
return result;
if (!isStatic)
return null;
ClassDescriptor[] interfaces = getInterfaceDescriptorList();
for (ClassDescriptor implementedInterface : interfaces) {
superClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, implementedInterface);
result = superClass.findField(name, signature, isStatic);
if (result != null)
return result;
}
return null;
} catch (CheckedAnalysisException e) {
return null;
}
}
/**
* @return Returns the immediateEnclosingClass.
*/
public ClassDescriptor getImmediateEnclosingClass() {
return immediateEnclosingClass;
}
/*
* (non-Javadoc)
*
* @see edu.umd.cs.findbugs.ba.AccessibleEntity#getPackageName()
*/
@Override
public String getPackageName() {
String dottedClassName = getClassDescriptor().toDottedClassName();
int lastDot = dottedClassName.lastIndexOf('.');
if (lastDot < 0) {
return "";
} else {
return dottedClassName.substring(0, lastDot);
}
}
/*
* (non-Javadoc)
*
* @see edu.umd.cs.findbugs.ba.AccessibleEntity#getPackageName()
*/
public String getSlashedPackageName() {
String slashedClassName = getClassDescriptor().getClassName();
int lastSlash = slashedClassName.lastIndexOf('/');
if (lastSlash < 0) {
return "";
} else {
return slashedClassName.substring(0, lastSlash);
}
}
public Collection<ClassDescriptor> getAnnotationDescriptors() {
return classAnnotations.keySet();
}
public Collection<AnnotationValue> getAnnotations() {
return classAnnotations.values();
}
public AnnotationValue getAnnotation(ClassDescriptor desc) {
return classAnnotations.get(desc);
}
/**
* Destructively add an annotation to the object. In general, this is not a
* great idea, since it could cause the same class to appear to have
* different annotations at different times. However, this method is
* necessary for "built-in" annotations that FindBugs adds to system
* classes. As long as we add such annotations early enough that nobody will
* notice, we should be ok.
*
* @param annotationValue
* an AnnotationValue to add to the class
*/
public void addAnnotation(AnnotationValue annotationValue) {
HashMap<ClassDescriptor, AnnotationValue> updatedMap = new HashMap<ClassDescriptor, AnnotationValue>(classAnnotations);
updatedMap.put(annotationValue.getAnnotationClass(), annotationValue);
classAnnotations = Util.immutableMap(updatedMap);
}
public ElementType getElementType() {
if (getClassName().endsWith("package-info"))
return ElementType.PACKAGE;
else if (isAnnotation())
return ElementType.ANNOTATION_TYPE;
return ElementType.TYPE;
}
public @CheckForNull
String getSource() {
return source;
}
@CheckForNull
AnnotatedObject containingScope;
private boolean containingScopeCached = false;
public @CheckForNull
AnnotatedObject getContainingScope() {
if (!containingScopeCached) {
containingScope = getContainingScope0();
containingScopeCached = true;
}
return containingScope;
}
public @CheckForNull
AnnotatedObject getContainingScope0() {
try {
if (immediateEnclosingClass != null) {
return Global.getAnalysisCache().getClassAnalysis(XClass.class, getImmediateEnclosingClass());
}
if (getClassName().endsWith("package-info")) {
return null;
}
ClassDescriptor p = DescriptorFactory.createClassDescriptor(getSlashedPackageName() + "/" + "package-info");
return Global.getAnalysisCache().getClassAnalysis(XClass.class, p);
} catch (CheckedAnalysisException e) {
return null;
}
}
/*
* (non-Javadoc)
*
* @see edu.umd.cs.findbugs.ba.XClass#getSourceSignature()
*/
public String getSourceSignature() {
return classSourceSignature;
}
public boolean usesConcurrency() {
return usesConcurrency;
}
public boolean hasStubs() {
return hasStubs;
}
}