v/* * Created on 08.01.2004 * */ package de.fhg.iese.Homes.eclipse.javaMetrics.qmoodMetrics; import java.util.ArrayList; import java.util.List; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IPackageBinding; import org.eclipse.jdt.core.Flags; /** * @author Sebastian Weber * */ public class MetricProvider extends ASTVisitor { private IType type; private String name; private ICompilationUnit cu; private int numberMethods; private int publicMethods = 0; private int inheritedMethods; private int inheritedFields = 0; private boolean isClass; private ArrayList ancestors; private ArrayList polymorphismSet; private int fieldCount = 0; private int protectedFieldCount = 0; private IJavaProject project; private ITypeBinding typeBinding; private double DAM; private double MFM; private double MFA; private int MOA = 0; private ArrayList userDefinedTypes; public MetricProvider() { ancestors = new ArrayList(); polymorphismSet = new ArrayList(); userDefinedTypes = new ArrayList(); numberMethods = -1; boolean isClass = false; } public void calculateMetrics(IType type, ICompilationUnit cu) { this.cu = cu; this.type = type; project = cu.getJavaProject(); name = type.getElementName(); try { isClass = type.isClass(); //fieldCount = type.getFields().length; // traverse compilation unit and identify type // definition (IType type) CompilationUnit ast = AST.parseCompilationUnit(cu, true); ast.accept(this); // compute complex metric value computeCIS(); computeMFM(); computeDAM(); computeNIM(); computeNOP(); } catch (JavaModelException e) { e.printStackTrace(); } } private void computeCIS() { try { IMethod[] methods = type.getMethods(); numberMethods = methods.length; // all methods in interfaces are implicit public // (whether the public modifier is explicit given or not) if (type.isInterface()) { publicMethods = methods.length; return; } IMethod method; int modifiers; int i; for (i = 0; i < numberMethods; i++) { method = methods[i]; modifiers = method.getFlags(); if (isPublic(modifiers)) publicMethods++; } } catch (JavaModelException e) { System.out.println("error while trying to get all methods"); e.printStackTrace(); } } private boolean isPublic(int modifiers) { if (Flags.isPublic(modifiers)) return true; return false; } public boolean visit(TypeDeclaration node) { String currentName = node.getName().getIdentifier(); if (currentName.equals(name)) { // get possible interfaces typeBinding = node.resolveBinding(); //System.out.println("get ancestors of " + name); Name superclass = node.getSuperclass(); if (superclass != null) { ITypeBinding binding = superclass.resolveTypeBinding(); countAncestors(binding); } CloserVisitor closerVisitor = new CloserVisitor(); node.accept(closerVisitor); } return true; } // TODO: unnecessary overhead...add element to ancestor list // in computeNOP() ! private void countAncestors(ITypeBinding binding) { if (binding != null) { /*String currentName = binding.getName(); System.out.println("superklasse: " + currentName);*/ ancestors.add(new SuperClass(binding)); ITypeBinding next = binding.getSuperclass(); countAncestors(next); } } public int getNumberMethods() { return numberMethods; } public int getInterfaceSize() { return publicMethods; } public boolean isClass() { return isClass; } public double getDataAccessMetric() { return DAM; } private double computeDAM() { DAM = 0; //System.out.println("class: " + name + ", all fields: " + fieldCount + ", -/#: " + protectedFieldCount); if (fieldCount > 0) DAM = (double) protectedFieldCount / (double) fieldCount; return DAM; } public int getProtectedData() { return protectedFieldCount; } public int getData() { return fieldCount; } public int getInheritedData() { return inheritedFields; } public int getNumberAncestors() { int size = ancestors.size(); // this type inherits implicitly from Object. // thus the number of ancestors is always >= 1 if (size == 0) return 1; return size; } public int getInheritedMethodCount() { return inheritedMethods; } private void computeNIM() { inheritedMethods = 0; SuperClass current; int i; int size = ancestors.size(); for (i = 0; i < size; i++) { current = (SuperClass) ancestors.get(i); //System.out.println("superklasse: " + current.getName() + ", #M: " + current.getNumberMethods() + ", #F: " + current.getFieldCount()); inheritedMethods += current.getNumberMethods(); } } public double getFunctionalModularity() { return MFM; } private void computeMFM() { MFM = 0; if (fieldCount > 0) { SuperClass current; int i; int size = ancestors.size(); for (i = 0; i < size; i++) { current = (SuperClass) ancestors.get(i); inheritedFields += current.getFieldCount(); } double x = inheritedFields; double y = fieldCount; double overall = x + y; MFM = x / overall; //System.out.println("class: " + name + " #fields: " + fieldCount + " #inherited: " + inheritedFields); } } public double getFunctionalAbstraction() { return MFA; } private void computeMFA() { MFA = 0; if (numberMethods > 0) { inheritedMethods = getInheritedMethodCount(); double x = inheritedMethods; double y = numberMethods; double overall = inheritedMethods + numberMethods; MFA = x / overall; } } /** * method getNumberPolymorphicVarieties * returns the number of varieties this class * can adopt. * * @return varieties */ public int getNumberPolymorphicVarieties() { // return polymorphicVarities; return polymorphismSet.size(); } private void computeNOP() { // start with base type computeNOP(typeBinding); } private void computeNOP(ITypeBinding binding) { // add binding to set insertSet(binding); // check for interfaces (type == class) // or check for superinterfaces (type == interface). // returns empty list if type (class) doesn't implement // interfaces or if type (interface) doesn't extend // interfaces. ITypeBinding[] interfaces = binding.getInterfaces(); int size = interfaces.length; // there is an implements clause (class) or an extends clausse (interface)! if (size > 0) { int i; for (i = 0; i < size; i++) { // check recursive every interface for superinterfaces computeNOP(interfaces[i]); } } // check for a super class ITypeBinding superClass = binding.getSuperclass(); // if type == interface than superClass == null. If // type == class and the class extends an other class // than superClass != null. if (superClass != null) { computeNOP(superClass); } } public int getAggregation() { return MOA; } // TODO: geht noch net public void calculateMOA(QMOODMetrics qmoodMetrics) { int i; int size = userDefinedTypes.size(); String currentType; for (i = 0; i < size; i++) { currentType = (String) userDefinedTypes.get(i); if (qmoodMetrics.isUserDefined(project.getElementName(), currentType)) { //System.out.println("user package? " + currentType); MOA++; } } } private void insertSet(ITypeBinding binding) { String name = binding.getQualifiedName(); String current; int i; int size = polymorphismSet.size(); for (i = 0; i < size; i++) { current = (String) polymorphismSet.get(i); // do not insert into set. element already exists! if (current.equals(name)) return; } // new element polymorphismSet.add(name); } public SuperClass[] getAncestors() { int i; int size = ancestors.size(); SuperClass[] superClasses = new SuperClass[size]; for (i = 0; i < size; i++) { superClasses[i] = (SuperClass) ancestors.get(i); } return superClasses; } class CloserVisitor extends ASTVisitor { public boolean visit(FieldDeclaration node) { System.out.println("class: " + cu.getElementName()); List list = node.fragments(); int i, size; size = list.size(); VariableDeclarationFragment fragment; for (i = 0; i < size; i++) { fragment = (VariableDeclarationFragment) list.get(i); fieldCount++; System.out.println("identifier: " + fragment.getName().getIdentifier()); } return true; } /*public boolean visit(FieldDeclaration node) { fieldCount++; String n = ""; if (node.getType().resolveBinding() != null) n = node.getType().resolveBinding().getName(); System.out.println("class: " + name + ", field: " + n); resolveModifiers(node.getFlags()); return true; }*/ private void resolveModifiers(int flags) { boolean protectedModifier; boolean privateModifier; protectedModifier = false; privateModifier = false; if (Flags.isPrivate(flags)) privateModifier = true; if (Flags.isProtected(flags)) protectedModifier = true; if (privateModifier | protectedModifier) protectedFieldCount++; } public boolean visit(VariableDeclarationStatement node) { List liveList = node.fragments(); if (liveList.size() > 0) { VariableDeclarationFragment fragment =(VariableDeclarationFragment) liveList.get(0); IVariableBinding variableBinding = fragment.resolveBinding(); if (variableBinding != null) { ITypeBinding binding = variableBinding.getType(); if (binding != null) { // check if this package is an user defined package // than the type (which is defined in this package) is user // defined, too. // Therefor save package name in a list. after all compilation units // are proceeded computeMOA() will be invoked and the list is used to // compute MOA value IPackageBinding packageBinding = binding.getPackage(); if (packageBinding != null) userDefinedTypes.add(packageBinding.getName()); } } } return true; } // TODO: !!! public boolean visit(VariableDeclarationExpression node) { return true; } } } class SuperClass { private String name; private int methodCount; private int fieldCount; public SuperClass(ITypeBinding binding) { name = binding.getName(); IMethodBinding[] methods = binding.getDeclaredMethods(); IVariableBinding[] fields = binding.getDeclaredFields(); methodCount = methods.length; fieldCount = fields.length; } public String getName() { return name; } public int getNumberMethods() { return methodCount; } public int getFieldCount() { return fieldCount; } }