View Javadoc

    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 && 	//Don't analyse enums, which cannot subclass clone()
    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 }