package jdepend.metadata;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import jdepend.framework.log.LogUtil;
import jdepend.metadata.annotation.AnnotationDefs;
import jdepend.metadata.annotation.AnnotationParse;
import jdepend.metadata.annotation.RequestMapping;
import jdepend.metadata.annotation.Transactional;
import jdepend.metadata.util.JavaClassCollection;
import jdepend.metadata.util.MethodUtil;
import jdepend.metadata.util.ParseUtil;
import jdepend.metadata.util.SignatureUtil;
import org.apache.bcel.classfile.AnnotationEntry;
import org.apache.bcel.classfile.Utility;
public class Method extends AccessFlags {
/**
*
*/
private static final long serialVersionUID = -8642392143295770051L;
private String javaClassId;
private String name;
private String signature;
private String info;
private Collection<InvokeItem> invokeItems;
private Collection<Attribute> readFields;
private Collection<Attribute> writeFields;
private int selfLineCount;
private int argumentCount;
private AnnotationDefs annotationDefs;
private transient Collection<String> argTypes;
private transient Collection<String> returnTypes;
private transient Collection<JavaClass> argClassTypes;
private transient Collection<JavaClass> returnClassTypes;
private transient Collection<Method> invokeMethods;
private transient Collection<InvokeItem> invokedItems;
private transient Collection<Method> invokedMethods;
private transient Collection<InvokeItem> cascadeInvokedItems;
private transient Collection<Method> cascadeInvokedMethods;
private transient JavaClass javaClass;
public final static String CLINIT = "<clinit>";
public final static String INIT = "<init>";
public Method() {
}
public Method(JavaClass javaClass, org.apache.bcel.classfile.Method method, boolean isParseAnnotation) {
this.javaClass = javaClass;
this.javaClassId = javaClass.getId();
this.access_flags = method.getAccessFlags();
this.name = method.getName();
this.signature = SignatureUtil.getSignature(method);
this.info = ParseUtil.getMethodInfo(method.toString());
this.argumentCount = this.calArgumentCount();
this.invokeItems = new ArrayList<InvokeItem>();
this.readFields = new ArrayList<Attribute>();
this.writeFields = new ArrayList<Attribute>();
this.selfLineCount = -1;
this.invokedItems = new HashSet<InvokeItem>();
this.annotationDefs = new AnnotationDefs();
// 处理Annotation
if (isParseAnnotation) {
for (AnnotationEntry annotationEntry : method.getAnnotationEntries()) {
if (annotationEntry.getAnnotationType().equals(AnnotationParse.Transactional)) {
this.annotationDefs.setTransactional(AnnotationParse.parseTransactional(annotationEntry));
} else if (annotationEntry.getAnnotationType().equals(AnnotationParse.RequestMapping)) {
this.annotationDefs.setRequestMapping(AnnotationParse.parseRequestMapping(annotationEntry));
}
}
}
}
public Method(String javaClassId, Method method) {
this.javaClassId = javaClassId;
this.access_flags = method.access_flags;
this.name = method.name;
this.signature = method.signature;
this.info = method.info;
this.argumentCount = method.argumentCount;
this.invokedItems = new HashSet<InvokeItem>();
this.invokeItems = method.invokeItems;
for (InvokeItem invokeItem : this.invokeItems) {
invokeItem.setCaller(this);
}
this.readFields = method.readFields;
this.writeFields = method.writeFields;
this.selfLineCount = method.selfLineCount;
this.annotationDefs = method.annotationDefs;
}
public String getAccessFlagName() {
return Utility.accessToString(this.getAccessFlags());
}
public String getName() {
return name;
}
public String getPath() {
return this.javaClass.getName() + "." + this.name;
}
public JavaClass getJavaClass() {
return javaClass;
}
public void setJavaClass(JavaClass javaClass) {
this.javaClass = javaClass;
}
public String getInfo() {
return info;
}
public Transactional getTransactional() {
return this.annotationDefs.getTransactional();
}
public Collection<InvokeItem> getInvokeItems() {
return invokeItems;
}
public synchronized Collection<Method> getInvokeMethods() {
if (this.invokeMethods == null) {
this.invokeMethods = new HashSet<Method>();
for (InvokeItem item : this.invokeItems) {
this.invokeMethods.add(item.getCallee());
}
}
return this.invokeMethods;
}
public Collection<InvokeItem> getInvokedItems() {
return invokedItems;
}
public synchronized void addInvokedItem(InvokeItem invokeItem) {
invokedItems.add(invokeItem);
}
public synchronized Collection<Method> getInvokedMethods() {
if (this.invokedMethods == null) {
this.invokedMethods = new HashSet<Method>();
for (InvokeItem item : this.invokedItems) {
this.invokedMethods.add(item.getCaller());
}
}
return invokedMethods;
}
public synchronized Collection<InvokeItem> getCascadeInvokedItems() {
if (cascadeInvokedItems == null) {
cascadeInvokedItems = new HashSet<InvokeItem>();
cascadeInvokedItems.addAll(this.getInvokedItems());
for (Method method : this.getInvokedMethods()) {
cascadeInvokedItems.addAll(method.getInvokedItems());
cascadeInvokedItems.addAll(method.getCascadeInvokedItems());
}
}
return cascadeInvokedItems;
}
public synchronized Collection<Method> getCascadeInvokedMethods() {
if (cascadeInvokedMethods == null) {
cascadeInvokedMethods = new HashSet<Method>();
for (InvokeItem invokeItem : getCascadeInvokedItems()) {
cascadeInvokedMethods.add(invokeItem.getCaller());
}
}
return cascadeInvokedMethods;
}
public Collection<Attribute> getReadFields() {
return readFields;
}
public Collection<Attribute> getWriteFields() {
return writeFields;
}
public synchronized void addInvokeItem(InvokeItem item) {
if (!this.invokeItems.contains(item)) {
this.invokeItems.add(item);
item.setCaller(this);
}
}
public void addReadField(String fieldName) {
Attribute attribute = this.javaClass.getTheAttribute(fieldName);
if (attribute != null && !this.readFields.contains(attribute)) {
this.readFields.add(attribute);
}
}
public void addWriteField(String fieldName) {
Attribute attribute = this.javaClass.getTheAttribute(fieldName);
if (attribute != null && !this.writeFields.contains(attribute)) {
this.writeFields.add(attribute);
}
}
public String getSignature() {
return signature;
}
public synchronized Collection<String> getArgumentTypes() {
if (this.argTypes == null) {
this.argTypes = new HashSet<String>();
try {
int pos = signature.lastIndexOf(')');
if (pos > 0) {
String paramTypes = signature.substring(1, pos);
Collection<String> types = ParseUtil.signatureToTypes(paramTypes);
for (String type : types) {
this.argTypes.add(type);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return this.argTypes;
}
public int getArgumentCount() {
return argumentCount;
}
public String getArgumentInfo() {
return this.info.substring(this.info.indexOf('(') + 1, this.info.indexOf(')'));
}
public synchronized Collection<String> getReturnTypes() {
if (this.returnTypes == null) {
this.returnTypes = new HashSet<String>();
try {
String returnType = signature.substring(signature.lastIndexOf(')') + 1);
Collection<String> types = ParseUtil.signatureToTypes(returnType);
for (String type : types) {
this.returnTypes.add(type);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return this.returnTypes;
}
public Collection<JavaClass> getArgClassTypes() {
return argClassTypes;
}
public void setArgClassTypes(Collection<JavaClass> argClassTypes) {
this.argClassTypes = argClassTypes;
}
public Collection<JavaClass> getReturnClassTypes() {
return returnClassTypes;
}
public void setReturnClassTypes(Collection<JavaClass> returnClassTypes) {
this.returnClassTypes = returnClassTypes;
}
public boolean existReturn() {
return this.getReturnTypes().size() > 0;
}
public boolean isConstruction() {
return this.name.equals(INIT);
}
public int getSelfLineCount() {
return selfLineCount;
}
public void setSelfLineCount(int selfLineCount) {
this.selfLineCount = selfLineCount;
}
public RequestMapping getRequestMapping() {
return this.annotationDefs.getRequestMapping();
}
public String getRequestMappingValue() {
if (this.javaClass.getDetail().getRequestMapping() == null || this.getRequestMapping() == null) {
return null;
}
StringBuilder value = new StringBuilder();
if (this.javaClass.getDetail().getRequestMapping().getValue().length() > 0) {
if (this.javaClass.getDetail().getRequestMapping().getValue().startsWith("/")) {
value.append(this.javaClass.getDetail().getRequestMapping().getValue());
} else {
value.append("/");
value.append(this.javaClass.getDetail().getRequestMapping().getValue());
}
}
if (this.getRequestMapping().getValue().length() > 0) {
if (this.getRequestMapping().getValue().startsWith("/")) {
value.append(this.getRequestMapping().getValue());
} else {
value.append("/");
value.append(this.getRequestMapping().getValue());
}
}
return value.toString();
}
public String getRequestMappingValueNoVariable() {
String requestMappingValue = this.getRequestMappingValue();
if (requestMappingValue == null) {
return null;
} else {
if (requestMappingValue.indexOf("{") == -1) {
return requestMappingValue;
} else {
StringBuilder valueNoVariable = new StringBuilder();
valueNoVariable.append("/");
for (String segment : requestMappingValue.split("/")) {
if (segment.length() > 0 && !segment.startsWith("{")) {
valueNoVariable.append(segment);
valueNoVariable.append("/");
}
}
if (!requestMappingValue.endsWith("/")) {
valueNoVariable.delete(valueNoVariable.length() - 1, valueNoVariable.length());
}
return valueNoVariable.toString();
}
}
}
public boolean isOverride(Method method) {
if (!this.name.equals(method.name)) {
return false;
}
return this.signature.equals(method.signature);
}
public String getJavaClassId() {
return javaClassId;
}
public boolean isRemoteInvokeItem() {
for (InvokeItem item : this.getInvokeItems()) {
if (item instanceof RemoteInvokeItem) {
return true;
}
}
return false;
}
public boolean isRemoteInvokedItem() {
for (InvokeItem item : this.getInvokedItems()) {
if (item instanceof RemoteInvokeItem) {
return true;
}
}
return false;
}
public float getStability() {
int totalCoupling = this.getInvokedMethods().size() + this.getInvokeMethods().size();
if (totalCoupling > 0) {
return (float) this.getInvokeMethods().size() / (float) totalCoupling;
} else {
return 0.5F;
}
}
public void supply(JavaClassCollection javaClasses) {
Collection<JavaClass> argumentTypes;
JavaClass argumentTypeClass;
Collection<JavaClass> returnTypes;
JavaClass returnTypeClass;
// 填充JavaClass
if (this.javaClass == null) {
this.javaClass = javaClasses.getTheClass(this.javaClassId);
}
// 填充参数
argumentTypes = new HashSet<JavaClass>();
for (String type : this.getArgumentTypes()) {
argumentTypeClass = javaClasses.getTheClass(javaClass.getPlace(), type);
if (argumentTypeClass != null) {
argumentTypes.add(argumentTypeClass);
}
}
this.setArgClassTypes(argumentTypes);
// 填充返回值
returnTypes = new HashSet<JavaClass>();
for (String type : this.getReturnTypes()) {
returnTypeClass = javaClasses.getTheClass(javaClass.getPlace(), type);
if (returnTypeClass != null) {
returnTypes.add(returnTypeClass);
}
}
this.setReturnClassTypes(returnTypes);
}
public void supplyInvokeItem(JavaClassCollection javaClasses) {
Iterator<InvokeItem> it;
InvokeItem invokeItem;
Collection<InvokeItem> newInvokeItems = new HashSet<InvokeItem>();
// 填充Method中的InvokeItem
it = this.getInvokeItems().iterator();
while (it.hasNext()) {
invokeItem = it.next();
invokeItem.setCaller(this);
if (!invokeItem.supplyCallee(javaClasses)) {
LogUtil.getInstance(Method.class).systemWarning(
"Method [" + this.getJavaClass().getName() + "." + this.getInfo() + "] invokeItem ["
+ invokeItem + "] not found, is removed.");
it.remove();
} else {
InvokeItem newInvokeItem = invokeItem.transform();
if (newInvokeItem != null) {
// 删除原invokeItem的相关引用
it.remove();
invokeItem.getCallee().getInvokedItems().remove(invokeItem);
// 增加新的invokeItem
newInvokeItems.add(newInvokeItem);
}
}
}
// 增加新的invokeItems
for (InvokeItem newInvokeItem : newInvokeItems) {
this.getInvokeItems().add(newInvokeItem);
newInvokeItem.getCallee().getInvokedItems().add(newInvokeItem);
}
}
public void filterExternalJavaClass(Collection<JavaClass> javaClasses) {
for (JavaClass typeClass : this.argClassTypes) {
if (!javaClasses.contains(typeClass)) {
this.argClassTypes.remove(typeClass);
}
}
for (JavaClass typeClass : this.returnClassTypes) {
if (!javaClasses.contains(typeClass)) {
this.returnClassTypes.remove(typeClass);
}
}
InvokeItem invokeItem;
Iterator<InvokeItem> it = this.getInvokeItems().iterator();
while (it.hasNext()) {
invokeItem = it.next();
if (!javaClasses.contains(invokeItem.getCallee().getJavaClass())) {
it.remove();
}
}
}
public String getMethodInfo() {
StringBuilder info = new StringBuilder();
info.append(this.javaClass.getName());
info.append(".");
info.append(this.name);
String argumentInfo = this.getArgumentInfo();
if (argumentInfo != null && argumentInfo.length() > 0) {
info.append("(");
info.append(argumentInfo);
info.append(")");
}
return info.toString();
}
public boolean isBusinessMethod() {
return MethodUtil.isBusinessMethod(this);
}
@Override
public String toString() {
StringBuilder info1 = new StringBuilder();
info1.append("Method [" + info + " Class=" + this.getJavaClass().getName() + "]");
info1.append("\n Signature : " + this.signature + "]");
info1.append("\n selfLineCount : " + this.selfLineCount);
info1.append("\n argumentCount : " + this.getArgumentCount());
if (this.getArgumentTypes().size() > 0) {
info1.append("\n argumentTypes:");
for (String type : this.getArgumentTypes()) {
info1.append("\n " + type);
}
}
if (this.getReturnTypes().size() > 0) {
info1.append("\n returnTypes:");
for (String type : this.getReturnTypes()) {
info1.append("\n " + type);
}
}
if (this.annotationDefs.getTransactional() != null) {
info1.append("\n Transactional:");
info1.append(this.annotationDefs.getTransactional());
info1.append("\n");
}
if (this.getRequestMapping() != null) {
info1.append("\n requestMapping : " + this.getRequestMappingValue());
info1.append(" method : " + this.getRequestMapping().getMethod());
}
if (this.readFields.size() != 0) {
info1.append("\n readFields:");
for (Attribute attribute : this.readFields) {
info1.append("\n ");
info1.append(attribute);
}
}
if (this.writeFields.size() != 0) {
info1.append("\n writeFields:");
for (Attribute attribute : this.writeFields) {
info1.append("\n ");
info1.append(attribute);
}
}
if (this.invokeItems.size() != 0) {
info1.append("\n invokeItems:");
for (InvokeItem item : this.invokeItems) {
info1.append("\n ");
info1.append(item);
}
}
if (this.getInvokedItems().size() != 0) {
info1.append("\n invokedItems:");
for (InvokeItem item : this.getInvokedItems()) {
info1.append("\n ");
info1.append(item);
}
}
return info1.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((info == null) ? 0 : info.hashCode());
result = prime * result + ((javaClassId == null) ? 0 : javaClassId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (getClass() != obj.getClass())
return false;
Method other = (Method) obj;
if (!javaClassId.equals(other.javaClassId))
return false;
if (!info.equals(other.info))
return false;
return true;
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
this.invokedItems = new HashSet<InvokeItem>();
}
private boolean isInvoked(Method method) {
if (method.getInvokeMethods().contains(this)) {
return true;
}
for (Method overridedMethod : this.javaClass.getOverridedMethods(this)) {
if (method.getInvokeMethods().contains(overridedMethod)) {
return true;
}
}
for (Method subOverrideMethod : this.javaClass.getSubOverrideMethods(this)) {
if (method.getInvokeMethods().contains(subOverrideMethod)) {
return true;
}
}
return false;
}
private int calArgumentCount() {
int pos = this.info.indexOf('(');
int pos1 = this.info.indexOf(')');
if (pos + 1 == pos1) {
return 0;
} else {
int count = 1;
pos1 = this.info.indexOf(',', pos + 1);
while (pos1 != -1) {
pos = pos1;
pos1 = this.info.indexOf(',', pos + 1);
count++;
}
return count;
}
}
}