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 net.sourceforge.pmd.AbstractRule; 7 import net.sourceforge.pmd.ast.ASTBlock; 8 import net.sourceforge.pmd.ast.ASTBlockStatement; 9 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration; 10 import net.sourceforge.pmd.ast.ASTClassOrInterfaceType; 11 import net.sourceforge.pmd.ast.ASTExtendsList; 12 import net.sourceforge.pmd.ast.ASTFormalParameters; 13 import net.sourceforge.pmd.ast.ASTImplementsList; 14 import net.sourceforge.pmd.ast.ASTMethodDeclaration; 15 import net.sourceforge.pmd.ast.ASTMethodDeclarator; 16 import net.sourceforge.pmd.ast.SimpleNode; 17 18 import java.util.Arrays; 19 import java.util.List; 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 */ 31 public class CloneMethodMustImplementCloneable extends AbstractRule { 32 33 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { 34 ASTImplementsList impl = (ASTImplementsList) 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 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 71 if (node.isFinal()) { 72 List blocks = node.findChildrenOfType(ASTBlock.class); 73 if (blocks.size() == 1) { 74 blocks = node.findChildrenOfType(ASTBlockStatement.class); 75 if (blocks.size() == 1) { 76 ASTBlockStatement block = (ASTBlockStatement) blocks.get(0); 77 ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) 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 }