View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.typeresolution.rules;
5   
6   import java.util.Arrays;
7   import java.util.List;
8   
9   import net.sourceforge.pmd.AbstractJavaRule;
10  import net.sourceforge.pmd.ast.ASTBlock;
11  import net.sourceforge.pmd.ast.ASTBlockStatement;
12  import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
13  import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
14  import net.sourceforge.pmd.ast.ASTExtendsList;
15  import net.sourceforge.pmd.ast.ASTFormalParameters;
16  import net.sourceforge.pmd.ast.ASTImplementsList;
17  import net.sourceforge.pmd.ast.ASTMethodDeclaration;
18  import net.sourceforge.pmd.ast.ASTMethodDeclarator;
19  import net.sourceforge.pmd.ast.SimpleNode;
20  
21  /**
22   * The method clone() should only be implemented if the class implements the
23   * Cloneable interface with the exception of a final method that only throws
24   * CloneNotSupportedException. This version uses PMD's type resolution
25   * facilities, and can detect if the class implements or extends a Cloneable
26   * class
27   * 
28   * @author acaplan
29   */
30  public class CloneMethodMustImplementCloneable extends AbstractJavaRule {
31  
32      @SuppressWarnings("unchecked")
33  	public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
34          ASTImplementsList impl = node.getFirstChildOfType(ASTImplementsList.class);
35          if (impl != null && impl.jjtGetParent().equals(node)) {
36              for (int ix = 0; ix < impl.jjtGetNumChildren(); ix++) {
37                  ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) impl.jjtGetChild(ix);
38                  if (type.getType() == null) {
39                      if ("Cloneable".equals(type.getImage())) {
40                          return data;
41                      }
42                  } else if (type.getType().equals(Cloneable.class)) {
43                      return data;
44                  } else {
45                      List<Class> implementors = Arrays.asList(type.getType().getInterfaces());
46                      if (implementors.contains(Cloneable.class)) {
47                          return data;
48                      }
49                  }
50              }
51          }
52          if (node.jjtGetNumChildren() != 0 && node.jjtGetChild(0).getClass().equals(ASTExtendsList.class)) {
53              ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) ((SimpleNode) node.jjtGetChild(0)).jjtGetChild(0);
54              Class clazz = type.getType();
55              if (clazz != null && clazz.equals(Cloneable.class)) {
56                  return data;
57              }
58              while (clazz != null && !Object.class.equals(clazz)) {
59                  if (Arrays.asList(clazz.getInterfaces()).contains(Cloneable.class)) {
60                      return data;
61                  }
62                  clazz = clazz.getSuperclass();
63              }
64          }
65  
66          return super.visit(node, data);
67      }
68  
69      public Object visit(ASTMethodDeclaration node, Object data) {
70          ASTClassOrInterfaceDeclaration classOrInterface = node.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class);
71  		if (classOrInterface != null && 	//Don't analyse enums, which cannot subclass clone()
72  				(node.isFinal() || classOrInterface.isFinal())) {
73              if (node.findChildrenOfType(ASTBlock.class).size() == 1) {
74                  List<ASTBlockStatement> blocks = node.findChildrenOfType(ASTBlockStatement.class);
75                  if (blocks.size() == 1) {
76                      ASTBlockStatement block = blocks.get(0);
77                      ASTClassOrInterfaceType type = block.getFirstChildOfType(ASTClassOrInterfaceType.class);
78                      if (type != null && type.getType() != null && type.getNthParent(9).equals(node) && type.getType().equals(CloneNotSupportedException.class)) {
79                          return data;
80                      } else if (type != null && type.getType() == null && "CloneNotSupportedException".equals(type.getImage())) {
81                          return data;
82                      }
83                  }
84              }
85          }
86          return super.visit(node, data);
87      }
88  
89      public Object visit(ASTMethodDeclarator node, Object data) {
90          if (!"clone".equals(node.getImage())) {
91              return data;
92          }
93          int countParams = ((ASTFormalParameters) node.jjtGetChild(0)).jjtGetNumChildren();
94          if (countParams != 0) {
95              return data;
96          }
97          addViolation(data, node);
98          return data;
99      }
100 }