1
2
3
4 package net.sourceforge.pmd.lang.java.rule.coupling;
5
6 import java.util.HashSet;
7 import java.util.Set;
8
9 import net.sourceforge.pmd.lang.ast.Node;
10 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
11 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
12 import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
13 import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
14 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
15 import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
16 import net.sourceforge.pmd.lang.java.ast.ASTReferenceType;
17 import net.sourceforge.pmd.lang.java.ast.ASTResultType;
18 import net.sourceforge.pmd.lang.java.ast.ASTType;
19 import net.sourceforge.pmd.lang.java.ast.JavaNode;
20 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
21 import net.sourceforge.pmd.lang.java.symboltable.ClassScope;
22 import net.sourceforge.pmd.lang.rule.properties.IntegerProperty;
23
24
25
26
27
28
29
30
31
32
33
34 public class CouplingBetweenObjectsRule extends AbstractJavaRule {
35
36 private int couplingCount;
37 private Set<String> typesFoundSoFar;
38
39 private static final IntegerProperty THRESHOLD_DESCRIPTOR = new IntegerProperty(
40 "threshold", "Unique type reporting threshold", 2, 100, 20, 1.0f
41 );
42
43 public CouplingBetweenObjectsRule() {
44 definePropertyDescriptor(THRESHOLD_DESCRIPTOR);
45 }
46
47 @Override
48 public Object visit(ASTCompilationUnit cu, Object data) {
49 typesFoundSoFar = new HashSet<String>();
50 couplingCount = 0;
51
52 Object returnObj = cu.childrenAccept(this, data);
53
54 if (couplingCount > getProperty(THRESHOLD_DESCRIPTOR)) {
55 addViolation(data, cu, "A value of " + couplingCount + " may denote a high amount of coupling within the class");
56 }
57
58 return returnObj;
59 }
60
61 @Override
62 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
63 if (node.isInterface()) {
64 return data;
65 }
66 return super.visit(node, data);
67 }
68
69 @Override
70 public Object visit(ASTResultType node, Object data) {
71 for (int x = 0; x < node.jjtGetNumChildren(); x++) {
72 Node tNode = node.jjtGetChild(x);
73 if (tNode instanceof ASTType) {
74 Node reftypeNode = tNode.jjtGetChild(0);
75 if (reftypeNode instanceof ASTReferenceType) {
76 Node classOrIntType = reftypeNode.jjtGetChild(0);
77 if (classOrIntType instanceof ASTClassOrInterfaceType) {
78 Node nameNode = classOrIntType;
79 this.checkVariableType(nameNode, nameNode.getImage());
80 }
81 }
82 }
83 }
84 return super.visit(node, data);
85 }
86
87 @Override
88 public Object visit(ASTLocalVariableDeclaration node, Object data) {
89 handleASTTypeChildren(node);
90 return super.visit(node, data);
91 }
92
93 @Override
94 public Object visit(ASTFormalParameter node, Object data) {
95 handleASTTypeChildren(node);
96 return super.visit(node, data);
97 }
98
99 @Override
100 public Object visit(ASTFieldDeclaration node, Object data) {
101 for (int x = 0; x < node.jjtGetNumChildren(); ++x) {
102 Node firstStmt = node.jjtGetChild(x);
103 if (firstStmt instanceof ASTType) {
104 ASTType tp = (ASTType) firstStmt;
105 Node nd = tp.jjtGetChild(0);
106 checkVariableType(nd, nd.getImage());
107 }
108 }
109
110 return super.visit(node, data);
111 }
112
113
114
115
116
117 private void handleASTTypeChildren(Node node) {
118 for (int x = 0; x < node.jjtGetNumChildren(); x++) {
119 Node sNode = node.jjtGetChild(x);
120 if (sNode instanceof ASTType) {
121 Node nameNode = sNode.jjtGetChild(0);
122 checkVariableType(nameNode, nameNode.getImage());
123 }
124 }
125 }
126
127
128
129
130
131
132
133 private void checkVariableType(Node nameNode, String variableType) {
134
135 if (nameNode.getParentsOfType(ASTClassOrInterfaceDeclaration.class).isEmpty()) {
136 return;
137 }
138
139
140 ClassScope clzScope = ((JavaNode)nameNode).getScope().getEnclosingScope(ClassScope.class);
141 if (!clzScope.getClassName().equals(variableType) && !this.filterTypes(variableType) && !this.typesFoundSoFar.contains(variableType)) {
142 couplingCount++;
143 typesFoundSoFar.add(variableType);
144 }
145 }
146
147
148
149
150
151
152
153
154 private boolean filterTypes(String variableType) {
155 return variableType != null && (variableType.startsWith("java.lang.") || variableType.equals("String") || filterPrimitivesAndWrappers(variableType));
156 }
157
158
159
160
161
162 private boolean filterPrimitivesAndWrappers(String variableType) {
163 return "int".equals(variableType)
164 || "Integer".equals(variableType)
165 || "char".equals(variableType)
166 || "Character".equals(variableType)
167 || "double".equals(variableType)
168 || "long".equals(variableType)
169 || "short".equals(variableType)
170 || "float".equals(variableType)
171 || "byte".equals(variableType)
172 || "boolean".equals(variableType);
173 }
174 }