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