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