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 &&
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 }