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