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; } } }