aspect ChidamberAndKemererJavaMetrics { /** This program is heavily influenced by ckjm (http://www.spinellis.gr/sw/ckjm/) For each class the following six metrics proposed by Chidamber and Kemerer are computed: WMC: Weighted methods per class DIT: Depth of Inheritance Tree NOC: Number of Children CBO: Coupling between object classes RFC: Response for a Class LCOM: Lack of cohesion in methods In addition it also calculates for each class Ca: Afferent couplings NPM: Number of public methods */ /* -------------- WMC - WEIGTED METHODS PER CLASS ---------------------- */ syn int TypeDecl.getNumberMethods() = methodsCounter().value(); coll Counter TypeDecl.methodsCounter() [new Counter()] with add; MethodDecl contributes 1 to TypeDecl.methodsCounter() for hostType(); ConstructorDecl contributes 1 to TypeDecl.methodsCounter() for hostType(); /* ---------------- DIT - DEPTH OF INHERITANCE TREE ---------------- */ syn lazy int TypeDecl.getDepthOfInheritanceTree() = 0; eq ClassDecl.getDepthOfInheritanceTree() = hasSuperclass() ? (superclass().getDepthOfInheritanceTree() + 1) : 0; eq InterfaceDecl.getDepthOfInheritanceTree() = 1; /* ---------------- NOC - NUMBER OF CHILDREN ----------------------- */ syn lazy int TypeDecl.getNumberOfChildren() = nocCounter().value(); coll Counter TypeDecl.nocCounter() [new Counter()] with add; ClassDecl contributes 1 when hasSuperclass() to TypeDecl.nocCounter() for superclass(); InterfaceDecl contributes 1 to TypeDecl.nocCounter() for typeObject(); /* --------------- CBO - COUPLING BETWEEN OBJECT CLASSES ------------ */ syn int TypeDecl.getCouplingBetweenObjectClasses() = efferentSet().size(); // compute the set of reftypes that this type declaration depends on coll HashSet TypeDecl.efferentSet() [new HashSet()] with addAll; // reference types contributes dependencies to their supertypes TypeDecl contributes supertypes() to TypeDecl.efferentSet() for this; // all type names contribute dependencies to the type they are used in TypeAccess contributes type().elementType().asSet() when hostType() != null && type().elementType().isReferenceType() to TypeDecl.efferentSet() for hostType(); // a used field contributes a dependency to the type it is declared in VarAccess contributes decl().hostType().asSet() when isFieldAccess() to TypeDecl.efferentSet() for hostType(); // each method access contributes a dependency to the type the method is declared in MethodAccess contributes dependencies() to TypeDecl.efferentSet() for hostType(); // each dereferenced array reference contributes a dependency to its element type ArrayAccess contributes type().elementType().asSet() when type().elementType().isReferenceType() to TypeDecl.efferentSet() for hostType(); // each return statement contributes a dependency to the type of its returned value ReturnStmt contributes getResult().type().elementType().asSet() when hasResult() && getResult().type().elementType().isReferenceType() to TypeDecl.efferentSet() for hostBodyDecl().hostType(); // a class instantiation contributes dependencies to the instantiated class and all its argument types ClassInstanceExpr contributes dependencies() to TypeDecl.efferentSet() for hostType(); syn HashSet MethodAccess.dependencies() { HashSet result = new HashSet(); MethodDecl m = decl(); result.add(m.hostType()); if(m.type().elementType().isReferenceType()) result.add(m.type().elementType()); for(int i = 0; i < m.getNumParameter(); i++) if(m.getParameter(i).type().elementType().isReferenceType()) result.add(m.getParameter(i).type().elementType()); return result; } syn HashSet ClassInstanceExpr.dependencies() { HashSet result = new HashSet(); ConstructorDecl m = decl(); result.add(m.hostType()); for(int i = 0; i < m.getNumParameter(); i++) if(m.getParameter(i).type().elementType().isReferenceType()) result.add(m.getParameter(i).type().elementType()); return result; } // utility method that converts a node reference into a set with a single element public HashSet ASTNode.asSet() { HashSet result = new HashSet(); result.add(this); return result; } // compute the direct supertypes of a type declaration syn lazy HashSet TypeDecl.supertypes() = new HashSet(); // the supertypes of a class are the possible superclass and all implemented interfaces eq ClassDecl.supertypes() { HashSet result = new HashSet(); if(hasSuperclass()) result.add(superclass()); for(Iterator iter = interfacesIterator(); iter.hasNext(); ) result.add(iter.next()); return result; } // the supertypes of an interface are all extended interfaces and possibly object eq InterfaceDecl.supertypes() { HashSet result = new HashSet(); if(getNumSuperInterfaceId() == 0) result.add(typeObject()); for(Iterator iter = superinterfacesIterator(); iter.hasNext(); ) result.add(iter.next()); return result; } /* ---------------- RFC - RESPONSE FOR A CLASS ----------------------- */ syn int TypeDecl.getResponseForClass() = responseSet().size(); // compute the set of methods included in the response set of this type coll HashSet TypeDecl.responseSet() [new HashSet()] with add; // call to methods are included in the response set MethodAccess contributes decl() to TypeDecl.responseSet() for hostType(); // a class's own methods are included in the response set MethodDecl contributes this to TypeDecl.responseSet() for hostType(); /* -------------- LCOM - LACK OF COHESION IN METHODS ------------------- */ // the set of fields accessed from this method coll HashSet MethodDecl.accessedFields() [new HashSet()] with add; VarAccess contributes decl() when isFieldAccess() && enclosingBodyDecl() instanceof MethodDecl to MethodDecl.accessedFields() for enclosingBodyDecl() instanceof MethodDecl ? (MethodDecl)enclosingBodyDecl() : null; // does method m access a field that is also accessed by this method? syn lazy boolean MethodDecl.sharesFieldAccessWith(MethodDecl m) { HashSet set = m.accessedFields(); for(Iterator iter = accessedFields().iterator(); iter.hasNext(); ) if(set.contains(iter.next())) return true; return false; } // compute lack of cohesion metric syn int TypeDecl.getLackOfCohesion() { int n1 = 0; int n2 = 0; for(Iterator iter = localMethodsIterator(); iter.hasNext(); ) { MethodDecl m1 = (MethodDecl)iter.next(); for(Iterator innerIter = localMethodsIterator(); innerIter.hasNext(); ) { MethodDecl m2 = (MethodDecl)innerIter.next(); if(m1 != m2) { if(!m1.sharesFieldAccessWith(m2)) n1++; else n2++; } } } int n = (n1 - n2) / 2; return n < 0 ? 0 : n; } /* -------------- NPM - NUMBER OF PUBLIC METHODS ---------------------- */ syn int TypeDecl.getNumberPublicMethods() = publicMethodsCounter().value(); coll Counter TypeDecl.publicMethodsCounter() [new Counter()] with add; MethodDecl contributes 1 when isPublic() to TypeDecl.publicMethodsCounter() for hostType(); ConstructorDecl contributes 1 when isPublic() to TypeDecl.publicMethodsCounter() for hostType(); /* --------------- Ca - AFFERENT COUPLING BETWEEN OBJECT CLASSES ----- */ syn int TypeDecl.getAfferentCoupling() = afferentSet().size(); coll HashSet TypeDecl.afferentSet() [new HashSet()] with add; TypeDecl contributes this to TypeDecl.afferentSet() for each efferentSet(); class Counter { private int value; public Counter() { value = 0; } public void add(int value) { this.value += value; } public int value() { return value; } } }