|
| 1 | +package org.hjug.metrics.rules; |
| 2 | + |
| 3 | +import net.sourceforge.pmd.lang.ast.Node; |
| 4 | +import net.sourceforge.pmd.lang.java.ast.*; |
| 5 | +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; |
| 6 | +import net.sourceforge.pmd.lang.java.symboltable.ClassScope; |
| 7 | +import net.sourceforge.pmd.properties.PropertyBuilder; |
| 8 | +import net.sourceforge.pmd.properties.PropertyDescriptor; |
| 9 | +import net.sourceforge.pmd.properties.PropertyFactory; |
| 10 | +import net.sourceforge.pmd.properties.constraints.NumericConstraints; |
| 11 | + |
| 12 | +import java.util.HashSet; |
| 13 | +import java.util.List; |
| 14 | +import java.util.Set; |
| 15 | + |
| 16 | +/** |
| 17 | + * Copy of PMD's CouplingBetweenObjectsRule |
| 18 | + * but generates the originally intended message containing coupling count |
| 19 | + */ |
| 20 | +public class CBORule extends AbstractJavaRule { |
| 21 | + private int couplingCount; |
| 22 | + private Set<String> typesFoundSoFar; |
| 23 | + private static final PropertyDescriptor<Integer> THRESHOLD_DESCRIPTOR = ((PropertyBuilder.GenericPropertyBuilder)((PropertyBuilder.GenericPropertyBuilder)((PropertyBuilder.GenericPropertyBuilder) PropertyFactory.intProperty("threshold").desc("Unique type reporting threshold")).require(NumericConstraints.positive())).defaultValue(20)).build(); |
| 24 | + |
| 25 | + public CBORule() { |
| 26 | + this.definePropertyDescriptor(THRESHOLD_DESCRIPTOR); |
| 27 | + } |
| 28 | + |
| 29 | + public Object visit(ASTCompilationUnit cu, Object data) { |
| 30 | + this.typesFoundSoFar = new HashSet(); |
| 31 | + this.couplingCount = 0; |
| 32 | + Object returnObj = super.visit(cu, data); |
| 33 | + if (this.couplingCount > (Integer)this.getProperty(THRESHOLD_DESCRIPTOR)) { |
| 34 | + //only the line below is different from PMD's CouplingBetweenObjectsRule class |
| 35 | + this.addViolationWithMessage(data, cu, "A value of " + this.couplingCount + " may denote a high amount of coupling within the class"); |
| 36 | + } |
| 37 | + |
| 38 | + return returnObj; |
| 39 | + } |
| 40 | + |
| 41 | + public Object visit(ASTResultType node, Object data) { |
| 42 | + for(int x = 0; x < node.getNumChildren(); ++x) { |
| 43 | + Node tNode = node.getChild(x); |
| 44 | + if (tNode instanceof ASTType) { |
| 45 | + Node reftypeNode = tNode.getChild(0); |
| 46 | + if (reftypeNode instanceof ASTReferenceType) { |
| 47 | + Node classOrIntType = reftypeNode.getChild(0); |
| 48 | + if (classOrIntType instanceof ASTClassOrInterfaceType) { |
| 49 | + this.checkVariableType(classOrIntType, classOrIntType.getImage()); |
| 50 | + } |
| 51 | + } |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | + return super.visit(node, data); |
| 56 | + } |
| 57 | + |
| 58 | + public Object visit(ASTLocalVariableDeclaration node, Object data) { |
| 59 | + this.handleASTTypeChildren(node); |
| 60 | + return super.visit(node, data); |
| 61 | + } |
| 62 | + |
| 63 | + public Object visit(ASTFormalParameter node, Object data) { |
| 64 | + this.handleASTTypeChildren(node); |
| 65 | + return super.visit(node, data); |
| 66 | + } |
| 67 | + |
| 68 | + public Object visit(ASTFieldDeclaration node, Object data) { |
| 69 | + for(int x = 0; x < node.getNumChildren(); ++x) { |
| 70 | + Node firstStmt = node.getChild(x); |
| 71 | + if (firstStmt instanceof ASTType) { |
| 72 | + ASTType tp = (ASTType)firstStmt; |
| 73 | + Node nd = tp.getChild(0); |
| 74 | + this.checkVariableType(nd, nd.getImage()); |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + return super.visit(node, data); |
| 79 | + } |
| 80 | + |
| 81 | + private void handleASTTypeChildren(Node node) { |
| 82 | + for(int x = 0; x < node.getNumChildren(); ++x) { |
| 83 | + Node sNode = node.getChild(x); |
| 84 | + if (sNode instanceof ASTType) { |
| 85 | + Node nameNode = sNode.getChild(0); |
| 86 | + this.checkVariableType(nameNode, nameNode.getImage()); |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + } |
| 91 | + |
| 92 | + private void checkVariableType(Node nameNode, String variableType) { |
| 93 | + List<ASTClassOrInterfaceDeclaration> parentTypes = nameNode.getParentsOfType(ASTClassOrInterfaceDeclaration.class); |
| 94 | + if (!parentTypes.isEmpty()) { |
| 95 | + if (!((ASTClassOrInterfaceDeclaration)parentTypes.get(0)).isInterface()) { |
| 96 | + ClassScope clzScope = (ClassScope)((JavaNode)nameNode).getScope().getEnclosingScope(ClassScope.class); |
| 97 | + if (!clzScope.getClassName().equals(variableType) && !this.filterTypes(variableType) && !this.typesFoundSoFar.contains(variableType)) { |
| 98 | + ++this.couplingCount; |
| 99 | + this.typesFoundSoFar.add(variableType); |
| 100 | + } |
| 101 | + |
| 102 | + } |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + private boolean filterTypes(String variableType) { |
| 107 | + return variableType != null && (variableType.startsWith("java.lang.") || "String".equals(variableType) || this.filterPrimitivesAndWrappers(variableType)); |
| 108 | + } |
| 109 | + |
| 110 | + private boolean filterPrimitivesAndWrappers(String variableType) { |
| 111 | + return "int".equals(variableType) || "Integer".equals(variableType) || "char".equals(variableType) || "Character".equals(variableType) || "double".equals(variableType) || "long".equals(variableType) || "short".equals(variableType) || "float".equals(variableType) || "byte".equals(variableType) || "boolean".equals(variableType); |
| 112 | + } |
| 113 | +} |
0 commit comments