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.rule.design;
5   
6   import java.util.Arrays;
7   import java.util.List;
8   import java.util.Map;
9   
10  import net.sourceforge.pmd.lang.ast.Node;
11  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
12  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
13  import net.sourceforge.pmd.lang.java.ast.ASTName;
14  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
15  import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
16  import net.sourceforge.pmd.lang.java.ast.ASTResultType;
17  import net.sourceforge.pmd.lang.java.rule.AbstractInefficientZeroCheck;
18  import net.sourceforge.pmd.lang.java.symboltable.ClassScope;
19  import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence;
20  import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration;
21  import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
22  import net.sourceforge.pmd.util.CollectionUtil;
23  
24  /**
25   * Detect structures like "foo.size() == 0" and suggest replacing them with
26   * foo.isEmpty(). Will also find != 0 (replaceable with !isEmpty()).
27   * 
28   * @author Jason Bennett
29   */
30  public class UseCollectionIsEmptyRule extends AbstractInefficientZeroCheck {
31      
32      public boolean appliesToClassName(String name){
33          return CollectionUtil.isCollectionType(name, true);
34      }
35      
36      /**
37       * Determine if we're dealing with .size method
38       * 
39       * @param occ
40       *            The name occurrence
41       * @return true if it's .size, else false
42       */
43      public boolean isTargetMethod(JavaNameOccurrence occ) {
44          if (occ.getNameForWhichThisIsAQualifier() != null) {
45              if (occ.getLocation().getImage().endsWith(".size")) {
46                  return true;
47              }
48          }
49          return false;
50      }
51  
52      @Override
53      public List<String> getComparisonTargets() {
54          return Arrays.asList("0", "1");
55      }
56  
57      @Override
58      public Object visit(ASTPrimarySuffix node, Object data) {
59          if (node.getImage() != null && node.getImage().endsWith("size")) {
60  
61              ASTClassOrInterfaceType type = getTypeOfPrimaryPrefix(node);
62              if (type == null) {
63                  type = getTypeOfMethodCall(node);
64              }
65  
66              if (type != null && CollectionUtil.isCollectionType(type.getType(), true)) {
67                  Node expr = node.jjtGetParent().jjtGetParent();
68                  checkNodeAndReport(data, node, expr);
69              }
70          }
71          return data;
72      }
73  
74      private ASTClassOrInterfaceType getTypeOfMethodCall(ASTPrimarySuffix node) {
75          ASTClassOrInterfaceType type = null;
76          ASTName methodName = node.jjtGetParent()
77                  .getFirstChildOfType(ASTPrimaryPrefix.class)
78                  .getFirstChildOfType(ASTName.class);
79          if (methodName != null) {
80              ClassScope classScope = node.getScope().getEnclosingScope(ClassScope.class);
81              Map<MethodNameDeclaration, List<NameOccurrence>> methods = classScope.getMethodDeclarations();
82              for (Map.Entry<MethodNameDeclaration, List<NameOccurrence>> e : methods.entrySet()) {
83                  if (e.getKey().getName().equals(methodName.getImage())) {
84                      type = e.getKey().getNode()
85                          .getFirstParentOfType(ASTMethodDeclaration.class)
86                          .getFirstChildOfType(ASTResultType.class)
87                          .getFirstDescendantOfType(ASTClassOrInterfaceType.class);
88                      break;
89                  }
90              }
91          }
92          return type;
93      }
94  
95      private ASTClassOrInterfaceType getTypeOfPrimaryPrefix(ASTPrimarySuffix node) {
96          return node.jjtGetParent()
97                  .getFirstChildOfType(ASTPrimaryPrefix.class)
98                  .getFirstDescendantOfType(ASTClassOrInterfaceType.class);
99      }
100 }