001: /**
002: * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003: */package net.sourceforge.pmd.typeresolution.rules;
004:
005: import java.util.Arrays;
006: import java.util.List;
007:
008: import net.sourceforge.pmd.AbstractJavaRule;
009: import net.sourceforge.pmd.ast.ASTBlock;
010: import net.sourceforge.pmd.ast.ASTBlockStatement;
011: import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
012: import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
013: import net.sourceforge.pmd.ast.ASTExtendsList;
014: import net.sourceforge.pmd.ast.ASTFormalParameters;
015: import net.sourceforge.pmd.ast.ASTImplementsList;
016: import net.sourceforge.pmd.ast.ASTMethodDeclaration;
017: import net.sourceforge.pmd.ast.ASTMethodDeclarator;
018: import net.sourceforge.pmd.ast.SimpleNode;
019:
020: /**
021: * The method clone() should only be implemented if the class implements the
022: * Cloneable interface with the exception of a final method that only throws
023: * CloneNotSupportedException. This version uses PMD's type resolution
024: * facilities, and can detect if the class implements or extends a Cloneable
025: * class
026: *
027: * @author acaplan
028: */
029: public class CloneMethodMustImplementCloneable extends AbstractJavaRule {
030:
031: @SuppressWarnings("unchecked")
032: public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
033: ASTImplementsList impl = node
034: .getFirstChildOfType(ASTImplementsList.class);
035: if (impl != null && impl.jjtGetParent().equals(node)) {
036: for (int ix = 0; ix < impl.jjtGetNumChildren(); ix++) {
037: ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) impl
038: .jjtGetChild(ix);
039: if (type.getType() == null) {
040: if ("Cloneable".equals(type.getImage())) {
041: return data;
042: }
043: } else if (type.getType().equals(Cloneable.class)) {
044: return data;
045: } else {
046: List<Class> implementors = Arrays.asList(type
047: .getType().getInterfaces());
048: if (implementors.contains(Cloneable.class)) {
049: return data;
050: }
051: }
052: }
053: }
054: if (node.jjtGetNumChildren() != 0
055: && node.jjtGetChild(0).getClass().equals(
056: ASTExtendsList.class)) {
057: ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) ((SimpleNode) node
058: .jjtGetChild(0)).jjtGetChild(0);
059: Class clazz = type.getType();
060: if (clazz != null && clazz.equals(Cloneable.class)) {
061: return data;
062: }
063: while (clazz != null && !Object.class.equals(clazz)) {
064: if (Arrays.asList(clazz.getInterfaces()).contains(
065: Cloneable.class)) {
066: return data;
067: }
068: clazz = clazz.getSuperclass();
069: }
070: }
071:
072: return super .visit(node, data);
073: }
074:
075: public Object visit(ASTMethodDeclaration node, Object data) {
076: ASTClassOrInterfaceDeclaration classOrInterface = node
077: .getFirstParentOfType(ASTClassOrInterfaceDeclaration.class);
078: if (classOrInterface != null && //Don't analyse enums, which cannot subclass clone()
079: (node.isFinal() || classOrInterface.isFinal())) {
080: if (node.findChildrenOfType(ASTBlock.class).size() == 1) {
081: List<ASTBlockStatement> blocks = node
082: .findChildrenOfType(ASTBlockStatement.class);
083: if (blocks.size() == 1) {
084: ASTBlockStatement block = blocks.get(0);
085: ASTClassOrInterfaceType type = block
086: .getFirstChildOfType(ASTClassOrInterfaceType.class);
087: if (type != null
088: && type.getType() != null
089: && type.getNthParent(9).equals(node)
090: && type.getType().equals(
091: CloneNotSupportedException.class)) {
092: return data;
093: } else if (type != null
094: && type.getType() == null
095: && "CloneNotSupportedException".equals(type
096: .getImage())) {
097: return data;
098: }
099: }
100: }
101: }
102: return super .visit(node, data);
103: }
104:
105: public Object visit(ASTMethodDeclarator node, Object data) {
106: if (!"clone".equals(node.getImage())) {
107: return data;
108: }
109: int countParams = ((ASTFormalParameters) node.jjtGetChild(0))
110: .jjtGetNumChildren();
111: if (countParams != 0) {
112: return data;
113: }
114: addViolation(data, node);
115: return data;
116: }
117: }
|