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;
5   
6   import java.util.List;
7   
8   import net.sourceforge.pmd.lang.ast.Node;
9   import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression;
10  import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
11  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
12  import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
13  import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence;
14  import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
15  
16  /**
17   * Detects and flags the occurrences of specific method calls against an instance of
18   * a designated class. I.e. String.indexOf. The goal is to be able to suggest more
19   * efficient/modern ways of implementing the same function.
20   *
21   * Concrete subclasses are expected to provide the name of the target class and an
22   * array of method names that we are looking for. We then pass judgment on any literal
23   * arguments we find in the subclass as well.
24   *
25   * @author Brian Remedios
26   * @version $Revision$
27   */
28  public abstract class AbstractPoorMethodCall extends AbstractJavaRule {
29      //FIXME not sure the abstraction is generic enough to be reused as is.
30  
31      /**
32       * The name of the type the method will be invoked against.
33       * @return String
34       */
35      protected abstract String targetTypename();
36  
37      /**
38       * Return the names of all the methods we are scanning for, no brackets or
39       * argument types.
40       *
41       * @return String[]
42       */
43      protected abstract String[] methodNames();
44  
45      /**
46       * Returns whether the node being sent to the method is OK or not. Return
47       * true if you want to record the method call as a violation.
48       *
49       * @param arg the node to inspect
50       * @return boolean
51       */
52      protected abstract boolean isViolationArgument(Node arg);
53  
54      /**
55       * Returns whether the name occurrence is one of the method calls
56       * we are interested in.
57       *
58       * @param occurrence NameOccurrence
59       * @return boolean
60       */
61      private boolean isNotedMethod(NameOccurrence occurrence) {
62  
63          if (occurrence == null) {
64              return false;
65          }
66  
67          String methodCall = occurrence.getImage();
68          String[] methodNames = methodNames();
69  
70          for (String element : methodNames) {
71              if (methodCall.indexOf(element) != -1) {
72                  return true;
73              }
74          }
75          return false;
76      }
77  
78      /**
79       * Method visit.
80       * @param node ASTVariableDeclaratorId
81       * @param data Object
82       * @return Object
83       * @see net.sourceforge.pmd.lang.java.ast.JavaParserVisitor#visit(ASTVariableDeclaratorId, Object)
84       */
85      @Override
86      public Object visit(ASTVariableDeclaratorId node, Object data) {
87          if (!targetTypename().equals(node.getNameDeclaration().getTypeImage())) {
88              return data;
89          }
90  
91          for (NameOccurrence occ : node.getUsages()) {
92              JavaNameOccurrence jocc = (JavaNameOccurrence)occ;
93              if (isNotedMethod(jocc.getNameForWhichThisIsAQualifier())) {
94                  Node parent = jocc.getLocation().jjtGetParent().jjtGetParent();
95                  if (parent instanceof ASTPrimaryExpression) {
96                      // bail out if it's something like indexOf("a" + "b")
97                      if (parent.hasDescendantOfType(ASTAdditiveExpression.class)) {
98                          return data;
99                      }
100                     List<ASTLiteral> literals = parent.findDescendantsOfType(ASTLiteral.class);
101                     for (int l = 0; l < literals.size(); l++) {
102                         ASTLiteral literal = literals.get(l);
103                         if (isViolationArgument(literal)) {
104                             addViolation(data, jocc.getLocation());
105                         }
106                     }
107                 }
108             }
109         }
110         return data;
111     }
112 }