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.symboltable.ClassScope;
    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              addViolation(data, cu, "A value of " + this.couplingCount + " may denote a high amount of coupling within the class");
    45          }
    46
    47          return returnObj;
    48      }
    49
    50      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
    51          if (node.isInterface()) {
    52              return data;
    53          }
    54          return super.visit(node, data);
    55      }
    56
    57      public Object visit(ASTResultType node, Object data) {
    58          for (int x = 0; x < node.jjtGetNumChildren(); x++) {
    59              SimpleNode tNode = (SimpleNode) node.jjtGetChild(x);
    60              if (tNode instanceof ASTType) {
    61                  SimpleNode reftypeNode = (SimpleNode) tNode.jjtGetChild(0);
    62                  if (reftypeNode instanceof ASTReferenceType) {
    63                      SimpleNode classOrIntType = (SimpleNode) reftypeNode.jjtGetChild(0);
    64                      if (classOrIntType instanceof ASTClassOrInterfaceType) {
    65                          SimpleNode nameNode = (ASTClassOrInterfaceType) classOrIntType;
    66                          this.checkVariableType(nameNode, nameNode.getImage());
    67                      }
    68                  }
    69              }
    70          }
    71          return super.visit(node, data);
    72      }
    73
    74      public Object visit(ASTLocalVariableDeclaration node, Object data) {
    75          this.handleASTTypeChildren(node);
    76          return super.visit(node, data);
    77      }
    78
    79      public Object visit(ASTFormalParameter node, Object data) {
    80          this.handleASTTypeChildren(node);
    81          return super.visit(node, data);
    82      }
    83
    84      public Object visit(ASTFieldDeclaration node, Object data) {
    85          for (int x = 0; x < node.jjtGetNumChildren(); ++x) {
    86              SimpleNode firstStmt = (SimpleNode) node.jjtGetChild(x);
    87              if (firstStmt instanceof ASTType) {
    88                  ASTType tp = (ASTType) firstStmt;
    89                  SimpleNode nd = (SimpleNode) tp.jjtGetChild(0);
    90                  this.checkVariableType(nd, nd.getImage());
    91              }
    92          }
    93
    94          return super.visit(node, data);
    95      }
    96
    97      /***
    98       * convience method to handle hierarchy. This is probably too much
    99       * work and will go away once I figure out the framework
    100      */
    101     private void handleASTTypeChildren(SimpleNode node) {
    102         for (int x = 0; x < node.jjtGetNumChildren(); x++) {
    103             SimpleNode sNode = (SimpleNode) node.jjtGetChild(x);
    104             if (sNode instanceof ASTType) {
    105                 SimpleNode nameNode = (SimpleNode) sNode.jjtGetChild(0);
    106                 this.checkVariableType(nameNode, nameNode.getImage());
    107             }
    108         }
    109     }
    110
    111     /***
    112      * performs a check on the variable and updates the counter. Counter is
    113      * instance for a class and is reset upon new class scan.
    114      *
    115      * @param String variableType
    116      */
    117     private void checkVariableType(SimpleNode nameNode, String variableType) {
    118         // TODO - move this into the symbol table somehow?
    119         if (nameNode.getParentsOfType(ASTClassOrInterfaceDeclaration.class).isEmpty()) {
    120             return;
    121         }
    122         //if the field is of any type other than the class type
    123         //increment the count
    124         ClassScope clzScope = nameNode.getScope().getEnclosingClassScope();
    125         if (!clzScope.getClassName().equals(variableType) && (!this.filterTypes(variableType)) && !this.typesFoundSoFar.contains(variableType)) {
    126             this.couplingCount++;
    127             this.typesFoundSoFar.add(variableType);
    128         }
    129     }
    130
    131     /***
    132      * Filters variable type - we don't want primatives, wrappers, strings, etc.
    133      * This needs more work. I'd like to filter out super types and perhaps interfaces
    134      *
    135      * @param String variableType
    136      * @return boolean true if variableType is not what we care about
    137      */
    138     private boolean filterTypes(String variableType) {
    139         return variableType != null && (variableType.startsWith("java.lang.") || (variableType.equals("String")) || filterPrimitivesAndWrappers(variableType));
    140     }
    141
    142     /***
    143      * @param String variableType
    144      * @return boolean true if variableType is a primative or wrapper
    145      */
    146     private boolean filterPrimitivesAndWrappers(String variableType) {
    147         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"));
    148     }
    149 }