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