1 /***
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.rules;
5
6 import net.sourceforge.pmd.AbstractRule;
7 import net.sourceforge.pmd.RuleContext;
8 import net.sourceforge.pmd.ast.ASTClassDeclaration;
9 import net.sourceforge.pmd.ast.ASTCompilationUnit;
10 import net.sourceforge.pmd.ast.ASTFieldDeclaration;
11 import net.sourceforge.pmd.ast.ASTFormalParameter;
12 import net.sourceforge.pmd.ast.ASTLocalVariableDeclaration;
13 import net.sourceforge.pmd.ast.ASTName;
14 import net.sourceforge.pmd.ast.ASTResultType;
15 import net.sourceforge.pmd.ast.ASTType;
16 import net.sourceforge.pmd.ast.SimpleNode;
17
18 import java.util.HashSet;
19 import java.util.Set;
20
21
22 /***
23 * CouplingBetweenObjectsRule attempts to capture all unique Class attributes,
24 * local variables, and return types to determine how many objects a class is
25 * coupled to. This is only a guage and isn't a hard and fast rule. The threshold
26 * value is configurable and should be determined accordingly
27 *
28 * @since Feb 20, 2003
29 * @author aglover
30 *
31 */
32 public class CouplingBetweenObjectsRule extends AbstractRule {
33
34 private String className;
35 private int couplingCount;
36 private Set typesFoundSoFar;
37
38 /***
39 * handles the source file
40 *
41 * @return Object
42 * @param ASTCompilationUnit cu
43 * @param Object data
44 */
45 public Object visit(ASTCompilationUnit cu, Object data) {
46 this.typesFoundSoFar = new HashSet();
47 this.couplingCount = 0;
48
49 Object returnObj = cu.childrenAccept(this, data);
50
51 if (this.couplingCount > getIntProperty("threshold")) {
52 RuleContext ctx = (RuleContext) data;
53 ctx.getReport().addRuleViolation(createRuleViolation(ctx, cu.getBeginLine(), "A value of " + this.couplingCount + " may denote a high amount of coupling within the class"));
54 }
55
56 return returnObj;
57 }
58
59 /***
60 * handles class declaration. I need this to capture class name. I think
61 * there is probably a better way to capture it; however, I don't know the
62 * framework well enough yet...
63 *
64 * @return Object
65 * @param ASTClassDeclaration node
66 * @param Object data
67 */
68 public Object visit(ASTClassDeclaration node, Object data) {
69 SimpleNode firstStmt = (SimpleNode) node.jjtGetChild(0);
70 this.className = firstStmt.getImage();
71 return super.visit(node, data);
72 }
73
74 /***
75 * handles a return type of a method
76 *
77 * @return Object
78 * @param ASTResultType node
79 * @param Object data
80 */
81 public Object visit(ASTResultType node, Object data) {
82 for (int x = 0; x < node.jjtGetNumChildren(); x++) {
83 SimpleNode tNode = (SimpleNode) node.jjtGetChild(x);
84 if (tNode instanceof ASTType) {
85 SimpleNode nameNode = (SimpleNode) tNode.jjtGetChild(0);
86 if (nameNode instanceof ASTName) {
87 this.checkVariableType(nameNode.getImage());
88 }
89 }
90 }
91 return super.visit(node, data);
92 }
93
94 /***
95 * handles a local variable found in a method block
96 *
97 * @return Object
98 * @param ASTLocalVariableDeclaration node
99 * @param Object data
100 */
101 public Object visit(ASTLocalVariableDeclaration node, Object data) {
102 this.handleASTTypeChildren(node);
103 return super.visit(node, data);
104 }
105
106 /***
107 * handles a method parameter
108 *
109 * @return Object
110 * @param ASTFormalParameter node
111 * @param Object data
112 */
113 public Object visit(ASTFormalParameter node, Object data) {
114 this.handleASTTypeChildren(node);
115 return super.visit(node, data);
116 }
117
118 /***
119 * handles a field declaration - i.e. an instance variable. Method doesn't care if variable
120 * is public/private/etc
121 *
122 * @return Object
123 * @param ASTFieldDeclaration node
124 * @param Object data
125 */
126 public Object visit(ASTFieldDeclaration node, Object data) {
127 for (int x = 0; x < node.jjtGetNumChildren(); ++x) {
128 SimpleNode firstStmt = (SimpleNode) node.jjtGetChild(x);
129 if (firstStmt instanceof ASTType) {
130 ASTType tp = (ASTType) firstStmt;
131 SimpleNode nd = (SimpleNode) tp.jjtGetChild(0);
132 this.checkVariableType(nd.getImage());
133 }
134 }
135
136 return super.visit(node, data);
137 }
138
139 /***
140 * convience method to handle hiearchy. This is probably too much
141 * work and will go away once I figure out the framework
142 *
143 */
144 private void handleASTTypeChildren(SimpleNode node) {
145 for (int x = 0; x < node.jjtGetNumChildren(); x++) {
146 SimpleNode sNode = (SimpleNode) node.jjtGetChild(x);
147 if (sNode instanceof ASTType) {
148 SimpleNode nameNode = (SimpleNode) sNode.jjtGetChild(0);
149 this.checkVariableType(nameNode.getImage());
150 }
151 }
152 }
153
154 /***
155 * performs a check on the variable and updates the couter. Counter is
156 * instance for a class and is reset upon new class scan.
157 *
158 * @param String variableType
159 */
160 private void checkVariableType(String variableType) {
161 //if the field is of any type other than the class type
162 //increment the count
163 if (!this.className.equals(variableType) && (!this.filterTypes(variableType)) && !this.typesFoundSoFar.contains(variableType)) {
164 this.couplingCount++;
165 this.typesFoundSoFar.add(variableType);
166 }
167 }
168
169 /***
170 * Filters variable type - we don't want primatives, wrappers, strings, etc.
171 * This needs more work. I'd like to filter out super types and perhaps interfaces
172 *
173 * @param String variableType
174 * @return boolean true if variableType is not what we care about
175 */
176 private boolean filterTypes(String variableType) {
177 return variableType.startsWith("java.lang.") || (variableType.equals("String")) || filterPrimativesAndWrappers(variableType);
178 }
179
180 /***
181 * @param String variableType
182 * @return boolean true if variableType is a primative or wrapper
183 */
184 private boolean filterPrimativesAndWrappers(String variableType) {
185 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"));
186 }
187 }
This page was automatically generated by Maven