View Javadoc

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.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  
19  import java.util.HashSet;
20  import java.util.Set;
21  
22  
23  /***
24   * CouplingBetweenObjects attempts to capture all unique Class attributes,
25   * local variables, and return types to determine how many objects a class is
26   * coupled to. This is only a guage and isn't a hard and fast rule. The threshold
27   * value is configurable and should be determined accordingly
28   *
29   * @author aglover
30   * @since Feb 20, 2003
31   */
32  public class CouplingBetweenObjects extends AbstractRule {
33  
34      private int couplingCount;
35      private Set typesFoundSoFar;
36  
37      public Object visit(ASTCompilationUnit cu, Object data) {
38          this.typesFoundSoFar = new HashSet();
39          this.couplingCount = 0;
40  
41          Object returnObj = cu.childrenAccept(this, data);
42  
43          if (this.couplingCount > getIntProperty("threshold")) {
44              RuleContext ctx = (RuleContext) data;
45              ctx.getReport().addRuleViolation(createRuleViolation(ctx, cu, "A value of " + this.couplingCount + " may denote a high amount of coupling within the class"));
46          }
47  
48          return returnObj;
49      }
50  
51      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
52          if (node.isInterface()) {
53              return data;
54          }
55          return super.visit(node, data);
56      }
57  
58      public Object visit(ASTResultType node, Object data) {
59          for (int x = 0; x < node.jjtGetNumChildren(); x++) {
60              SimpleNode tNode = (SimpleNode) node.jjtGetChild(x);
61              if (tNode instanceof ASTType) {
62                  SimpleNode reftypeNode = (SimpleNode) tNode.jjtGetChild(0);
63                  if (reftypeNode instanceof ASTReferenceType) {
64                      SimpleNode classOrIntType = (SimpleNode)reftypeNode.jjtGetChild(0);
65                      if (classOrIntType instanceof ASTClassOrInterfaceType) {
66                          SimpleNode nameNode = (ASTClassOrInterfaceType)classOrIntType;
67                          this.checkVariableType(nameNode, nameNode.getImage());
68                      }
69                  }
70              }
71          }
72          return super.visit(node, data);
73      }
74  
75      public Object visit(ASTLocalVariableDeclaration node, Object data) {
76          this.handleASTTypeChildren(node);
77          return super.visit(node, data);
78      }
79  
80      public Object visit(ASTFormalParameter node, Object data) {
81          this.handleASTTypeChildren(node);
82          return super.visit(node, data);
83      }
84  
85      public Object visit(ASTFieldDeclaration node, Object data) {
86          for (int x = 0; x < node.jjtGetNumChildren(); ++x) {
87              SimpleNode firstStmt = (SimpleNode) node.jjtGetChild(x);
88              if (firstStmt instanceof ASTType) {
89                  ASTType tp = (ASTType) firstStmt;
90                  SimpleNode nd = (SimpleNode) tp.jjtGetChild(0);
91                  this.checkVariableType(nd, nd.getImage());
92              }
93          }
94  
95          return super.visit(node, data);
96      }
97  
98      /***
99       * convience method to handle hierarchy. This is probably too much
100      * work and will go away once I figure out the framework
101      */
102     private void handleASTTypeChildren(SimpleNode node) {
103         for (int x = 0; x < node.jjtGetNumChildren(); x++) {
104             SimpleNode sNode = (SimpleNode) node.jjtGetChild(x);
105             if (sNode instanceof ASTType) {
106                 SimpleNode nameNode = (SimpleNode) sNode.jjtGetChild(0);
107                 this.checkVariableType(nameNode, nameNode.getImage());
108             }
109         }
110     }
111 
112     /***
113      * performs a check on the variable and updates the couter. Counter is
114      * instance for a class and is reset upon new class scan.
115      *
116      * @param String variableType
117      */
118     private void checkVariableType(SimpleNode nameNode, String variableType) {
119         //if the field is of any type other than the class type
120         //increment the count
121         if (!nameNode.getScope().getEnclosingClassScope().getClassName().equals(variableType) && (!this.filterTypes(variableType)) && !this.typesFoundSoFar.contains(variableType)) {
122             this.couplingCount++;
123             this.typesFoundSoFar.add(variableType);
124         }
125     }
126 
127     /***
128      * Filters variable type - we don't want primatives, wrappers, strings, etc.
129      * This needs more work. I'd like to filter out super types and perhaps interfaces
130      *
131      * @param String variableType
132      * @return boolean true if variableType is not what we care about
133      */
134     private boolean filterTypes(String variableType) {
135         return variableType != null && (variableType.startsWith("java.lang.") || (variableType.equals("String")) || filterPrimitivesAndWrappers(variableType));
136     }
137 
138     /***
139      * @param String variableType
140      * @return boolean true if variableType is a primative or wrapper
141      */
142     private boolean filterPrimitivesAndWrappers(String variableType) {
143         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"));
144     }
145 }