View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.java.rule.basic;
5   
6   import java.util.List;
7   
8   import net.sourceforge.pmd.lang.ast.Node;
9   import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
10  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
11  import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
12  import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters;
13  import net.sourceforge.pmd.lang.java.ast.ASTImplementsList;
14  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
15  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
16  
17  public class OverrideBothEqualsAndHashcodeRule extends AbstractJavaRule {
18  
19      private boolean implementsComparable = false;
20  
21      private boolean containsEquals = false;
22  
23      private boolean containsHashCode = false;
24  
25      private Node nodeFound = null;
26  
27      @Override
28      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
29          if (node.isInterface()) {
30              return data;
31          }
32          super.visit(node, data);
33          if (!implementsComparable && containsEquals ^ containsHashCode) {
34              if (nodeFound == null) {
35                  nodeFound = node;
36              }
37              addViolation(data, nodeFound);
38          }
39          implementsComparable = containsEquals = containsHashCode = false;
40          nodeFound = null;
41          return data;
42      }
43  
44      @Override
45      public Object visit(ASTImplementsList node, Object data) {
46          for (int ix = 0; ix < node.jjtGetNumChildren(); ix++) {
47              if (node.jjtGetChild(ix) instanceof ASTClassOrInterfaceType) {
48                  ASTClassOrInterfaceType cit = (ASTClassOrInterfaceType) node.jjtGetChild(ix);
49                  Class<?> clazz = cit.getType();
50                  if (clazz != null && node.jjtGetChild(ix).hasImageEqualTo("Comparable")) {
51                      implementsComparable = true;
52                      return data;
53                  }
54              }
55          }
56          return super.visit(node, data);
57      }
58  
59      @Override
60      public Object visit(ASTMethodDeclarator node, Object data) {
61          if (implementsComparable) {
62              return data;
63          }
64  
65          int iFormalParams = 0;
66          String paramName = null;
67          for (int ix = 0; ix < node.jjtGetNumChildren(); ix++) {
68              Node sn = node.jjtGetChild(ix);
69              if (sn instanceof ASTFormalParameters) {
70                  List<ASTFormalParameter> allParams = ((ASTFormalParameters) sn)
71                          .findChildrenOfType(ASTFormalParameter.class);
72                  for (ASTFormalParameter formalParam : allParams) {
73                      iFormalParams++;
74                      ASTClassOrInterfaceType param = formalParam.getFirstDescendantOfType(ASTClassOrInterfaceType.class);
75                      if (param != null) {
76                          paramName = param.getImage();
77                      }
78                  }
79              }
80          }
81  
82          if (iFormalParams == 0 && node.hasImageEqualTo("hashCode")) {
83              containsHashCode = true;
84              nodeFound = node;
85          } else if (iFormalParams == 1 && node.hasImageEqualTo("equals")
86                  && ("Object".equals(paramName) || "java.lang.Object".equals(paramName))) {
87              containsEquals = true;
88              nodeFound = node;
89          }
90          return super.visit(node, data);
91      }
92  
93  }