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